SpringSecurity原理剖析
一.前言
在SpringBoot大行其道的时代里,安全认证也被加入Spring家族中。SpringSecurity和Spring做了很好的集成,也是倍受欢迎。但是在使用SpringSecurity的时候会很苦恼,只是按照网上的帖子配置一下,但是具体的运行流程与原理很多人却是很蒙蔽。具体的原理也是一知半解的,源码看不懂,很多人在学习的道路上走着走着就不见了… 本人也苦于SpringSecurity原理的困扰很久,经过了九九八十一天的刻苦钻研,得到一些领悟,特此记录,以供日后翻阅。
目录中的前三章节在网上都可以随处搜索的到,本文重点讲解配置之外的深层次的原理。
一. 配置Spring Security的依赖
首先搭建一个SpringBoot项目,在该项目中引入Spring Security的依赖包,不多说,直接上pom的依赖配置
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- jwt token令牌 -->
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.8.3</version>
</dependency>
<!-- Spring security 安全认证 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
二.继承WebSecurityConfigurerAdapter 配置安全认证
@Component
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter; // JWT拦截器
@Autowired
private AjaxAuthenticationEntryPointHandler entryPointHandler;//未登陆handler
@Autowired
private AjaxAccessDeniedHandler accessDeniedHandler; //无权限访问handler
@Autowired
private AjaxAuthenticationFailureHandler failureHandler; //登陆失败handler
@Autowired
private AjaxLogoutSuccessHandler logoutHandler;//退出handler
@Autowired
private AjaxAuthenticationSuccessHandler successHandler;//登陆成功handler
@Autowired
private UserDetailsServerImpl detailsServer;
@Autowired
private PasswordEncoder passwordEncoder;
@Autowired
private SecurityProperties securityProperties;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(detailsServer).passwordEncoder(passwordEncoder);
}
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers(securityProperties.getUris().toArray(new String[0]));
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.httpBasic().authenticationEntryPoint(entryPointHandler)
.and()
.formLogin()
.successHandler(successHandler)
.failureHandler(failureHandler)
.and()
.logout()
.logoutSuccessHandler(logoutHandler)
.permitAll()
.and()
.authorizeRequests()
.anyRequest()
.access("@RBACAuthorityManager.hasPermission(request,authentication)")
.and()
.exceptionHandling().accessDeniedHandler(accessDeniedHandler);
http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
//http.userDetailsService(detailsServer);
}
public String encoder(String pwd) {
return passwordEncoder.encode(pwd);
}
}
三. Filter过滤器的加载
在以上的配置中,整个核心的安全配置都是通过HttpSecurity来完成的,究竟HttpSecurity为我们做了些什么呢?请看下方:
HttpSecurity 这个对象中维护了一个Filter数组,Spring在启动的时候会将那些配置的Filter加入到HttpSecurity这个Filter数组中。接下来我们看看有多少对象调用了this.filters.add(filter) 方法。
我们看到有各种的Configurer在调用HttpSecurity 对象的 addFilter(Filter filter) 方法,在往HttpSecurity 对象的fiters集合中添加Filter过滤器。
以CorsConfigurer 为例子,CorsConfigurer 继承了 AbstractHttpConfigurer,重写了configure方法,并在该方法中调用了HttpSecurity http.addFilter(corsFilter)。当我们给HttpSecurity 配置了.cors()的时候,那么这个CorsFilter就会被正式的加入到HttpSecurity中,并对所有的请求进行拦截。
四.Filter过滤器的排序
知道Filter过滤器的加载怎么加载到HttpSecurity之后,我们需要构建一个过滤器链DefaultSecurityFilterChain,
当后期所有的请求进来之后,都需要经过这个过滤器链的认证。
在这一步是对所有的过滤器进行一个排序,并形成一个过滤器链。具体的排序规则详见FilterComparator,在FilterComparator中已经规定好了各个过滤器的顺序, 如下图:
五. 过滤器链的排序
启动项目,在HttpSecurity的performBuid()方法打上断点,发现在此处对加入的所有的过滤器进行了一个排序操作。排序的规则和 FilterComparator一致。在我们这次的配置中,过滤器链中一共有13个过滤器,并且WebAsyncManagerIntegrationFilter在最前边。
六.过滤器对请求的处理
通过以上步骤,我们知道了当前项目中有13个过滤器,接下来依次对这13Filter进行打断点,向后台发送请求。
通过断点可以发现整个的请求顺序和DefaultSecurityFilterChain 中过滤器的顺序是一致的,WebAsyncManagerIntegrationFilter最先被处理,FilterSecurityInterceptor被最后处理。所有的Filter过滤器都通过了,才会进入到业务层。如果在某一个过滤器中验证失败,则该请求将会被拒绝。
到此一个完整的SpringSecurity请求拦截也就全部清楚了,在项目中到底有哪些过滤器,我们只需要打个断点看看过滤器链中有哪些,我们也可以自定义过滤器加入到HttpSecurity中即可。