在认证过程和访问授权前必须了解spring Security如何知道我们要求所有用户都经过身份验证? Spring Security如何知道我们想要支持基于表单的身份验证?因此必须了解WebSecurityConfigurerAdapter配置类如何工作的。而且也必须了解清楚filter的顺序,才能更好了解其调用工作流程。

1. WebSecurityConfigurerAdapter

在使用WebSecurityConfigurerAdapter前,先了解Spring security config。 Spring security config具有三个模块,一共有3个builder,认证相关的AuthenticationManagerBuilder和web相关的WebSecurity、HttpSecurity。

  1. AuthenticationManagerBuilder:用来配置全局的认证相关的信息,其实就是AuthenticationProvider和UserDetailsService,前者是认证服务提供商,后者是用户详情查询服务;

  2. WebSecurity: 全局请求忽略规则配置(比如说静态文件,比如说注册页面)、全局HttpFirewall配置、是否debug配置、全局SecurityFilterChain配置、privilegeEvaluator、expressionHandler、securityInterceptor;

  3. HttpSecurity:具体的权限控制规则配置。一个这个配置相当于xml配置中的一个标签。各种具体的认证机制的相关配置,OpenIDLoginConfigurer、AnonymousConfigurer、FormLoginConfigurer、HttpBasicConfigurer等。

WebSecurityConfigurerAdapter提供了简洁方式来创建WebSecurityConfigurer,其作为基类,可通过实现该类自定义配置类,主要重写这三个方法:

 protected void configure(AuthenticationManagerBuilder auth) throws Exception {}
 public void configure(WebSecurity web) throws Exception {}
 protected void configure(HttpSecurity httpSecurity) throws Exception {}

而且其自动从SpringFactoriesLoader查找AbstractHttpConfigurer让我们去扩展,想要实现必须创建一个AbstractHttpConfigurer的扩展类,并在classpath路径下创建一个文件META-INF/spring.factories。例如: org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer = sample.MyClassThatExtendsAbstractHttpConfigurer

其源码分析:

//1.init初始化:获取HttpSecurity和配置FilterSecurityInterceptor拦截器到WebSecurity
 public void init(final WebSecurity web) throws Exception {
         //获取HttpSecurity
   final HttpSecurity http = getHttp();
    //配置FilterSecurityInterceptor拦截器到WebSecurity
    web.addSecurityFilterChainBuilder(http).postBuildAction(new Runnable() {
        public void run() {
            FilterSecurityInterceptor securityInterceptor = http
                    .getSharedObject(FilterSecurityInterceptor.class);
            web.securityInterceptor(securityInterceptor);
        }
    });
}
......
//2.获取HttpSecurity的过程
protected final HttpSecurity getHttp() throws Exception {
if (http != null) {
    return http;
}
​
DefaultAuthenticationEventPublisher eventPublisher = objectPostProcessor
        .postProcess(new DefaultAuthenticationEventPublisher());
localConfigureAuthenticationBldr.authenticationEventPublisher(eventPublisher);
​
AuthenticationManager authenticationManager = authenticationManager();
authenticationBuilder.parentAuthenticationManager(authenticationManager);
Map<Class<? extends Object>, Object> sharedObjects = createSharedObjects();
​
http = new HttpSecurity(objectPostProcessor, authenticationBuilder,
        sharedObjects);
if (!disableDefaults) {
    // 默认的HttpSecurity的配置
    http
                //添加 CSRF 支持,使用WebSecurityConfigurerAdapter时,默认启用,禁用csrf().disable()
        .csrf().and()
        //添加WebAsyncManagerIntegrationFilter
        .addFilter(new WebAsyncManagerIntegrationFilter())
        //允许配置异常处理
        .exceptionHandling().and()
        //将安全标头添加到响应
        .headers().and()
        //允许配置会话管理
        .sessionManagement().and()
        //HttpServletRequest之间的SecurityContextHolder创建securityContext管理
        .securityContext().and()
        //允许配置请求缓存
        .requestCache().and()
        //允许配置匿名用户
        .anonymous().and()
        //HttpServletRequestd的方法和属性注册在SecurityContext中
        .servletApi().and()
        //使用默认登录页面
        .apply(new DefaultLoginPageConfigurer<>()).and()
        //提供注销支持
        .logout();
    // @formatter:on
    ClassLoader classLoader = this.context.getClassLoader();
    List<AbstractHttpConfigurer> defaultHttpConfigurers =
            SpringFactoriesLoader.loadFactories(AbstractHttpConfigurer.class, classLoader);
​
    for(AbstractHttpConfigurer configurer : defaultHttpConfigurers) {
        http.apply(configurer);
    }
}
configure(http);
return http;
}
...
//3.可重写方法实现自定义的

HttpSecurity protected void configure(HttpSecurity http) throws Exception {

logger.debug("Using default configure(HttpSecurity).If subclassed this will potentially override subclass configure(HttpSecurity).");

http
    .authorizeRequests()
        .anyRequest().authenticated()
        .and()
    .formLogin().and()
    .httpBasic();
}
....  

从源码init初始化模块中的“获取HttpSecurity”和“配置FilterSecurityInterceptor拦截器到WebSecurity”中可以看出,想要spring Security如何知道我们要求所有用户都经过身份验证? Spring Security如何知道我们想要支持基于表单的身份验证?只要重写protected void configure(HttpSecurity http) throws Exception方法即可。因此我们需要理解HttpSecurity的方法的作用,如何进行配置。下一节来讨论HttpSecurity。

2. HttpSecurity

HttpSecurity基于Web的安全性允许为特定的http请求进行配置。其有很多方法,列举一些常用的如下表:

HttpSecurity还有很多方法供我们使用,去配置HttpSecurity。由于太多这边就不一一说明,有兴趣可去研究。

3. WebSecurityConfigurerAdapter使用

WebSecurityConfigurerAdapter示例:

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
 @Autowired
 private MyFilterSecurityInterceptor myFilterSecurityInterceptor;
 protected void configure(HttpSecurity http) throws Exception {
     http
     //request 设置
     .authorizeRequests()   //http.authorizeRequests() 方法中的自定义匹配
     .antMatchers("/resources/**", "/signup", "/about").permitAll() // 指定所有用户进行访问指定的url          
     .antMatchers("/admin/**").hasRole("ADMIN")  //指定具有特定权限的用户才能访问特定目录,hasRole()方法指定用户权限,且不需前缀 “ROLE_“  
     .antMatchers("/db/**").access("hasRole('ADMIN') and hasRole('DBA')")//
     .anyRequest().authenticated()  //任何请求没匹配的都需要进行验证                                           
     .and()        //login设置  自定义登录页面且允许所有用户登录
     .formLogin()
     .loginPage("/login") //The updated configuration specifies the location of the log in page  指定自定义登录页面
     .permitAll(); // 允许所有用户访问登录页面. The formLogin().permitAll() 方法
     .and
     .logout()  //logouts 设置                                                              
     .logoutUrl("/my/logout")  // 指定注销路径                                              
     .logoutSuccessUrl("/my/index") //指定成功注销后跳转到指定的页面                                        
     .logoutSuccessHandler(logoutSuccessHandler)  //指定成功注销后处理类 如果使用了logoutSuccessHandler()的话, logoutSuccessUrl()就会失效                                
     .invalidateHttpSession(true)  // httpSession是否有效时间,如果使用了 SecurityContextLogoutHandler,其将被覆盖                                        
     .addLogoutHandler(logoutHandler)  //在最后增加默认的注销处理类LogoutHandler                
     .deleteCookies(cookieNamesToClear);//指定注销成功后remove cookies
     //增加在FilterSecurityInterceptor前添加自定义的myFilterSecurityInterceptor
     http.addFilterBefore(myFilterSecurityInterceptor, FilterSecurityInterceptor.class);
   }

NOTE:此示例只供参考

4. filter顺序

Spring Security filter顺序:

spring security的默认filter链:

 SecurityContextPersistenceFilter
->HeaderWriterFilter
->LogoutFilter
->UsernamePasswordAuthenticationFilter
->RequestCacheAwareFilter
->SecurityContextHolderAwareRequestFilter
->SessionManagementFilter
->ExceptionTranslationFilter
->FilterSecurityInterceptor

在上节我们已分析了核心的filter源码以及功能。可回看上节源码分析更加深入的了解各个filter工作原理。

总结:

在认证和访问授权过程前,首先必须进行WebSecurityConfigurer符合自身应用的security Configurer,也要清楚filter链的先后顺序,才能更好理解spring security的工作原理以及在项目中出现的问题定位。了解完准备工作,接下来将展开对认证和访问授权模块的工作流程研究以及项目示例分析。最后如有错误可评论告知。

12-04 02:15