Spring Security 实现IP白名单机制

本文讨论如何在Spring Security 中实现IP白名单机制。先看看默认实现机制,同时也讨论自定义AuthenticationProvider实现更加灵活的应用。

1. 默认实现

首先我们看下Java配置。使用hasIpAddress() 定义允许特定ip地址的用户访问特定资源。请看下面使用hasIpAddress()的配置:

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
 
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
          .antMatchers("/login").permitAll()
          .antMatchers("/foos/**").hasIpAddress("11.11.11.11")
          .anyRequest().authenticated()
          .and()
          .formLogin().permitAll()
          .and()
          .csrf().disable();
    }
 
    // ...
 
}

上面配置只有"11.11.11.11"地址的用户可以访问“/foos”的资源。使用白名单IP的用户在访问“/foos/”URL之前也不需要登录。如果需要先登录,那么配置表达式如下:

//...
.antMatchers("/foos/**")
.access("isAuthenticated() and hasIpAddress('11.11.11.11')")
//...

1.1. 测试

下面进行简单的测试,确保如预期的工作。首先确保任何用户登录后能访问主页:

@Test
public void givenUser_whenGetHomePage_thenOK() {
    Response response = RestAssured.given().auth().form("john", "123")
      .get("http://localhost:8082/");
 
    assertEquals(200, response.getStatusCode());
    assertTrue(response.asString().contains("Welcome"));
}

接下来,我们确保即使已认证用户不能访问“/foos”资源,除了白名单IP用户:

@Test
public void givenUserWithWrongIP_whenGetFooById_thenForbidden() {
    Response response = RestAssured.given().auth().form("john", "123")
      .get("http://localhost:8082/foos/1");
 
    assertEquals(403, response.getStatusCode());
    assertTrue(response.asString().contains("Forbidden"));
}

注意,我们不能从本地主机“127.0.0.1”访问“/foos”资源,只有具有“11.11.11.11”的用户才能访问它。

2. 自定义AuthenticationProvider实现

接下来看看如何通过自定义AuthenticationProvider实现更加灵活的限制。
我们已经看到hasIpAddress()方法及混合其它表达式实现,但有时需要更加定制化。
下面的示例我们有多个IP地址,只有这些IP地址的用户能访问系统:

@Component
public class CustomIpAuthenticationProvider implements AuthenticationProvider {
     
   Set<String> whitelist = new HashSet<String>();
 
    public CustomIpAuthenticationProvider() {
        whitelist.add("11.11.11.11");
        whitelist.add("12.12.12.12");
    }
 
    @Override
    public Authentication authenticate(Authentication auth) throws AuthenticationException {
        WebAuthenticationDetails details = (WebAuthenticationDetails) auth.getDetails();
        String userIp = details.getRemoteAddress();
        if(! whitelist.contains(userIp)){
            throw new BadCredentialsException("Invalid IP Address");
        }
        //...
}

现在我们在Spring Security配置中使用CustomIpAuthenticationProvider:

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
 
    @Autowired
    private CustomIpAuthenticationProvider authenticationProvider;
 
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
       auth.authenticationProvider(authenticationProvider);
    }
 
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
          .antMatchers("/login").permitAll()
          .anyRequest().authenticated()
          .and().formLogin().permitAll()
          .and().csrf().disable();
    }
 
}

这里使用WebAuthenticationDetails.getRemoteAddress() 方法获取用户IP地址。因此只有在白名单列表中的用户可以访问系统资源。

这仅仅是个基本实现,但我们可以自定义更强大的AuthenticationProvider。例如,我们可以在注册时存储带有用户详细信息的IP地址,并在验证期间在AuthenticationProvider中对其进行比较。

3. 总结

本文学习了两种方法实现IP白名单机制,尤其是自定义AuthenticationProvider机制功能很强大。

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐