在Spring Security中可以同时存在多个过滤器链,一个WebSecurityConfigurerAdapter的实例就可以配置一条过滤器链。

  我们来看如下一个案例:

@Configuration
public class SecurityConfig {
    @Bean
    UserDetailsService us(){
        InMemoryUserDetailsManager users = new InMemoryUserDetailsManager();
        users.createUser(User.withUsername("剑气近").password("{noop}123").roles("admin").build());
        return users;
    }
    @Configuration
    @Order(1)
    static class SecurityConfig01 extends WebSecurityConfigurerAdapter{
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            InMemoryUserDetailsManager users = new InMemoryUserDetailsManager();
            users.createUser(User.withUsername("chain1in").password("{noop}123").roles("admin").build());
            http.antMatcher("/bar/**")
                    .authorizeRequests()
                    .anyRequest().authenticated()
                    .and()
                    .formLogin()
                    .loginPage("/mylogin.html")
                    .loginProcessingUrl("/bar/login")
                    .successHandler(((req, resp, auth) -> {
                        resp.setContentType("application/json;charset=UTF-8");
                        String s = new ObjectMapper().writeValueAsString(auth);
                        resp.getWriter().write(s);
                    }))
                    .permitAll()
                    .and()
                    .csrf().disable()
                    .userDetailsService(users);
        }
    }
    @Configuration
    @Order(2)
    static class SecurityConfig02 extends WebSecurityConfigurerAdapter{
        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
            auth.inMemoryAuthentication().withUser("chain2out").password("{noop}123").roles("admin");
        }
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            InMemoryUserDetailsManager users = new InMemoryUserDetailsManager();
            users.createUser(User.withUsername("chain2in").password("{noop}123").roles("admin").build());
            http.antMatcher("/foo/**")
                    .authorizeRequests()
                    .anyRequest().authenticated()
                    .and()
                    .formLogin()
                    .loginPage("/mylogin.html")
                    .loginProcessingUrl("/foo/login")
                    .successHandler(((req, resp, auth) -> {
                        resp.setContentType("application/json;charset=UTF-8");
                        String s = new ObjectMapper().writeValueAsString(auth);
                        resp.getWriter().write(s);
                    }))
                    .permitAll()
                    .and()
                    .csrf().disable()
                    .userDetailsService(users);
        }
    }
}

  在SecurityConfig中分别定义两个静态内部类SecurityConfig01和SecurityConfig02,两个配置类都继承自WebSecurityConfigurerAdapter, 可以分别配置一条过滤器链。

  先来看Security01,在Security01中,我们设置过滤器链的拦截规则是/bar/**,即如果请求路径是/bar/**格式的,则进入到Security01的过滤器链中进行处理。同时我们配置了局部 AuthenticationManager 对应的用户是 chain1in/123 ,由于没有重写 configure(AuthenticationManagerBuilder)方法,所以注册到 Spring 容器中的 UserDetailsService 将作为局部 AuthenticationManager的parent对应的用户,换句话说,如果登录的路径是/bar/login,那么升发者可以使用 chain1in/123和 剑气近/123两个用户进行登录。

  再来看SecurityConfig02。在Security02中,我们设置过滤器链的拦截规则是/foo/**,即如果请求路径是/foo/**格式的,则进入到Secunty02的过滤器链中进行处理,同时我们配置了局部 AuthenticationManager 对应的用户是 chain2in/123 ,由于重写了 configure(Authentication ManagerBuilder)方法,在该方法中定义了局部AuthenticationManager的parent对应的用户,此时注册到Spring容器中的UserDetailsService实例对于/foo/**过滤器链不再生效。换句话说, 如果登录路径是/foo/login,开发者可以使用chain2in/123和 chain2out/123两个用户进行登录,而不可以使用 剑气近/123进行登录。

  需要注意的是,如果配置了多个过滤器链,需要使用@Order注解来标记不同配置的优先级(即不同过滤器链的优先级),数字越大优先级越低,当请求到来时,会按照过滤器链的优先级从高往低,依次进行匹配。