1. Overview

In this tutorial, we’ll discuss how to whitelist IP ranges in Spring Security.

We’ll take a look at both Java and XML configurations. We’ll also see how to whitelist IP range using a custom AuthenticationProvider.

2. Java Configuration

First, let’s explore the Java configuration.

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.authorizeHttpRequests(
                authorizationManagerRequestMatcherRegistry -> authorizationManagerRequestMatcherRegistry.requestMatchers("/login").permitAll()
                        .requestMatchers("/foos/**")
                        .access(new WebExpressionAuthorizationManager("isAuthenticated() and hasIpAddress('11.11.11.11')")).anyRequest().authenticated())
            .formLogin(AbstractAuthenticationFilterConfigurer::permitAll)
            .csrf(AbstractHttpConfigurer::disable);
        return http.build();
    }
}

In this configuration,  only users with the IP address “11.11.11.11” will be able to access the ”/foos” resource. There’s also no need for users with the whitelisted IP to log in before they access the “/foos/” URL.

If we want users with “11.11.11.11” IP to log in first, we can use the method in an expression of the form:

//...
.requestMatchers("/foos/**")
.access(new WebExpressionAuthorizationManager("isAuthenticated() and hasIpAddress('11.11.11.11')"))
//...

3. XML Configuration

Next, let’s see how to whitelist an IP range using XML configuration:

We’ll use hasIpAddress() here as well:

<security:http>
    <security:form-login/>
    <security:intercept-url pattern="/login" access="permitAll()" />
    <security:intercept-url pattern="/foos/**" access="hasIpAddress('11.11.11.11')" />
    <security:intercept-url pattern="/**" access="isAuthenticated()" />
</security:http>

// ...

4. Live Test

Now, here’s a simple live test to make sure everything is working properly.

First, we’ll make sure any user can access the home page after login:

@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"));
}

Next, we’ll make sure even authenticated users can’t access the “/foos” resource unless their IP is whitelisted:

@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"));
}

Note that we aren’t able to access “/foos” resource from localhost “127.0.0.1” as only users with “11.11.11.11” are able to access it.

5. Whitelisting Using a Custom AuthenticationProvider

Finally, we’ll see how to whitelist an IP range by building a custom AuthenticationProvider.

We’ve seen how we can use hasIpAddress() to whitelist an IP range and how to mix it with other expressions. But sometimes, we need more customization.

In the following example, we have multiple IP addresses whitelisted and only users from those IP addresses can log in to our system:

@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");
        }
        //...
}

Now, we’ll use our CustomIpAuthenticationProvider in our security configuration:

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Autowired
    private CustomIpAuthenticationProvider authenticationProvider;

    @Bean
    public InMemoryUserDetailsManager userDetailsService(HttpSecurity http) throws Exception {
        UserDetails user = User.withUsername("john")
            .password("{noop}123")
            .authorities("ROLE_USER")
            .build();
        http.getSharedObject(AuthenticationManagerBuilder.class)
            .authenticationProvider(authenticationProvider)
            .build();
        return new InMemoryUserDetailsManager(user);
    }

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.authorizeHttpRequests(
                authorizationManagerRequestMatcherRegistry -> authorizationManagerRequestMatcherRegistry.requestMatchers("/login").permitAll()
                        .requestMatchers("/foos/**")
                        .access(new WebExpressionAuthorizationManager("isAuthenticated() and hasIpAddress('11.11.11.11')")).anyRequest().authenticated())
            .formLogin(AbstractAuthenticationFilterConfigurer::permitAll)
            .csrf(AbstractHttpConfigurer::disable);
        return http.build();
    }
}

Here, we used the WebAuthenticationDetails getRemoteAddress() method to obtain a user’s IP address.

As a result, only users with whitelisted IP will be able to access our system.

This is a basic implementation, but we can customize our AuthenticationProvider as much as we want using a user’s IP. For example, we can store the IP address with user details on signup and compare it during authentication in our AuthenticationProvider.

6. Conclusion

We learned how to whitelist an IP range in Spring Security using Java and XML configuration. We also learned how to whitelist an IP range by building a custom AuthenticationProvider.

The full source code can be found over on GitHub.


» 下一篇: Ratpack HTTP客户端