前言
本文讨论HttpSecurity这个构建器中的配置器在HttpSecurity构建SecurityChainFilter的过程中做的事情。
HttpSecurity中配置器
比如在我写的例子中,HttpSecurity中有11个配置器。那么会顺序调度这11个配置器的init()、configurer()。
这里要强调的是,不是一个配置器对应一个过滤器。另外,配置的器的调度顺序和其生成的过滤器顺序无关,SecurityChainFilter过滤链中的过滤器顺序是由FilterComparator这个比较器决定的。
CsrfConfigurer
添加的过滤器
CsrfFilter
ExceptionHandlingConfigurer
添加的过滤器
ExceptionTranslationFilter
HeadersConfigurer
添加响应头。默认添加的结果是:
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Content-Type-Options: nosniff
Strict-Transport-Security: max-age=31536000 ; includeSubDomains
X-Frame-Options: DENY
X-XSS-Protection: 1; mode=block
SessionManagementConfigurer
添加的过滤器
SessionManagementFilter
ConcurrentSessionFilter
SecurityContextPersistenceFilter
添加的过滤器
SecurityContextPersistenceFilter
AnonymousConfigurer
添加的过滤器
AnonymousAuthenticationFilter
ServletApiConfigurer
添加的过滤器
SecurityContextHolderAwareRequestFilter
DefaultLoginPageConfigurer
添加的过滤器
DefaultLoginPageGeneratingFilter
LogoutConfigurer
添加的过滤器
LogoutFilter
FormLoginConfigurer
添加的过滤器
UsernamePasswordAuthenticationFilter
过滤器
除了我自定义的验证码过滤器(CustomCapchaFilter),其他的过滤器都是系统生成的。
SecurityContextPersistenceFilter
SecurityContextHolder是和当前线程关联的SecurityContext存放容器,SecurityContextRepository是和当前session关联的SecurityContext存放容器。
这个过滤器做的事情就是,使用SecurityContextRepository中获取的SecurityContext(不会返回null,如果不存在,会返回一个SecurityContext,这里面是一个空的Authtication)填充SecurityContextHolder。这样,在线程处理请求的任何一个地方,都可以通过它来获取、设置SecurityContext(特别是在我们的controller中,可以直接使用它获取当前用户)。当请求处理完成之后(经过了后续过滤链、controller、service等的处理),将SecurityContextHolder的数据保存到SecurityContextRepository中,清空SecurityContextHolder。
CsrfFilter
这个过滤器就是添加csrf的功能。
使用LazyCsrfTokenRepository包裹的HttpSessionCsrfTokenRepository将生成的CsrfToken存放在HttpSession中。当请求来的时候,判断session中有无CsrfToken,没有就生成一个,然后保存到session中。并且将生成的CsrfToken放在request对象中,默认key是“_csrf”。然后判断请求需不需要csrf校验,如果不需要则调下一个过滤器。如果需要,则判断用户的请求中请求头或请求体中的csrf与session中的时候相同,相同则校验通过。否则,调用accessDeniedHandler处理。
LogoutFilter
提供登出的功能。
判断请求是不是登出请求,如果是,迭代配置的所有LogoutHandler(比如在CsrfConfigurer中配置csrf过滤器时,给此过滤器的配置器添加了一个CsrfLogoutHandler,用于在登出的时候,清空csrfToken).
最后再调用logoutSuccessHandler。
UsernamePasswordAuthenticationFilter
登录认证(密码校验)。
严格来说,它并不是一个认证过滤器,它的父类AbstractAuthenticationProcessingFilter才是。判断请求是否是登录认证请求,如果是,则进行密码校验。具体进行验证的是AuthticationManager。认证成功之后将返回一个fully的Authtication对象。注意,认证成功之后,将执行sessionStrategy,默认是修改sessionId,预防session fixation攻击。并且在认证成功之后,是不继续走过滤链的,直接将请求交给successHandler处理了。
RequestCacheAwareFilter
判断当前请求对应的session中,有无key=SPRING_SECURITY_SAVED_REQUEST的对象,有则表示有保存的request对象,那么就使用保存的这个对象重新请求。
SecurityContextHolderAwareRequestFilter
拓展了httpServletRequest的功能。
HttpServletRequest.authenticate(HttpServletResponse)
HttpServletRequest.login(String, String)
HttpServletRequest.logout()
AsyncContext.start(Runnable)
AnonymousAuthenticationFilter
判断SecurityContextHolder中Authentication是否是null,如果是,则创建AnonymousAuthenticationToken。
SessionManagementConfigurer
不理解
ExceptionTranslationFilter
这个过滤器架起了java异常到Http response的桥梁。
如果遇到 AuthenticationException,交给authenticationEntryPoint处理
如果遇到 AccessDeniedException ,交给AccessDeniedHandler处理
FilterSecurityInterceptor
虽然这个类名字不是XXFilter,但他确实是一个过滤器。它不是由前面XXX配置器创建的。而是由AbstractInterceptUrlConfigurer创建的。它是整个权限校验的入口,另开一章吧。。。
@Override
public void configure(H http) throws Exception {
FilterInvocationSecurityMetadataSource metadataSource = createMetadataSource(http);
if (metadataSource == null) {
return;
}
FilterSecurityInterceptor securityInterceptor = createFilterSecurityInterceptor(
http, metadataSource, http.getSharedObject(AuthenticationManager.class));
if (filterSecurityInterceptorOncePerRequest != null) {
securityInterceptor
.setObserveOncePerRequest(filterSecurityInterceptorOncePerRequest);
}
securityInterceptor = postProcess(securityInterceptor);
http.addFilter(securityInterceptor);
http.setSharedObject(FilterSecurityInterceptor.class, securityInterceptor);
}