Search code examples
spring-mvcspring-securityspring-security4

When to use Spring Security`s antMatcher()?


When do we use antMatcher() vs antMatchers()?

For example:

http
   .antMatcher("/high_level_url_A/**")
   .authorizeRequests()
      .antMatchers("/high_level_url_A/sub_level_1").hasRole('USER')
      .antMatchers("/high_level_url_A/sub_level_2").hasRole('USER2')
      .somethingElse()
      .anyRequest().authenticated()
      .and()
   .antMatcher("/high_level_url_B/**")
   .authorizeRequests()
      .antMatchers("/high_level_url_B/sub_level_1").permitAll()
      .antMatchers("/high_level_url_B/sub_level_2").hasRole('USER3')
      .somethingElse()
      .anyRequest().authenticated()
      .and()
   ...

What I expect here is,

  • Any request matches to /high_level_url_A/** should be authenticated + /high_level_url_A/sub_level_1 only for USER and /high_level_url_A/sub_level_2 only for USER2
  • Any request matches to /high_level_url_B/** should be authenticated + /high_level_url_B/sub_level_1 for public access and /high_level_url_A/sub_level_2 only for USER3.
  • Any other pattern I don't care - But should be public ?

I have seen latest examples do not include antMatcher() these days. Why is that? Is antMatcher() no longer required?


Solution

  • You need antMatcher for multiple HttpSecurity, see Spring Security Reference:

    5.7 Multiple HttpSecurity

    We can configure multiple HttpSecurity instances just as we can have multiple <http> blocks. The key is to extend the WebSecurityConfigurationAdapter multiple times. For example, the following is an example of having a different configuration for URL’s that start with /api/.

    @EnableWebSecurity
    public class MultiHttpSecurityConfig {
      @Autowired
      public void configureGlobal(AuthenticationManagerBuilder auth) { 1
          auth
              .inMemoryAuthentication()
                  .withUser("user").password("password").roles("USER").and()
                  .withUser("admin").password("password").roles("USER", "ADMIN");
      }
    
      @Configuration
      @Order(1)                                                        2
      public static class ApiWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
          protected void configure(HttpSecurity http) throws Exception {
              http
                  .antMatcher("/api/**")                               3
                  .authorizeRequests()
                      .anyRequest().hasRole("ADMIN")
                      .and()
                  .httpBasic();
          }
      }    
    
      @Configuration                                                   4
      public static class FormLoginWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
    
          @Override
          protected void configure(HttpSecurity http) throws Exception {
              http
                  .authorizeRequests()
                      .anyRequest().authenticated()
                      .and()
                  .formLogin();
          }
      }
    }
    

    1 Configure Authentication as normal

    2 Create an instance of WebSecurityConfigurerAdapter that contains @Order to specify which WebSecurityConfigurerAdapter should be considered first.

    3 The http.antMatcher states that this HttpSecurity will only be applicable to URLs that start with /api/

    4 Create another instance of WebSecurityConfigurerAdapter. If the URL does not start with /api/ this configuration will be used. This configuration is considered after ApiWebSecurityConfigurationAdapter since it has an @Order value after 1 (no @Order defaults to last).

    In your case you need no antMatcher, because you have only one configuration. Your modified code:

    http
        .authorizeRequests()
            .antMatchers("/high_level_url_A/sub_level_1").hasRole('USER')
            .antMatchers("/high_level_url_A/sub_level_2").hasRole('USER2')
            .somethingElse() // for /high_level_url_A/**
            .antMatchers("/high_level_url_A/**").authenticated()
            .antMatchers("/high_level_url_B/sub_level_1").permitAll()
            .antMatchers("/high_level_url_B/sub_level_2").hasRole('USER3')
            .somethingElse() // for /high_level_url_B/**
            .antMatchers("/high_level_url_B/**").authenticated()
            .anyRequest().permitAll()