我们在以后的开发中可能需要在特定的过滤链之间添加自定义的FIlter,那么对于Spring Security我们需要了解到在整个执行过程中每一个过滤链的执行顺序和其功能
过滤链
在配置了Spring Security之后,我们可以通过日志查看加载了哪些过滤器
具体内容在源码【DefaultSecurityFilterChain】中
public DefaultSecurityFilterChain(RequestMatcher requestMatcher, List<Filter> filters) {
logger.info(LogMessage.format("Will secure %s with %s", requestMatcher, filters));
this.requestMatcher = requestMatcher;
this.filters = new ArrayList(filters);
}
日志记录输出如下:
o.s.s.web.DefaultSecurityFilterChain : Will secure any request with
[
org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@608c03e3,
org.springframework.security.web.context.SecurityContextPersistenceFilter@3de8e614,
org.springframework.security.web.header.HeaderWriterFilter@728cf276,
org.springframework.web.filter.CorsFilter@1979d616,
org.springframework.security.web.authentication.logout.LogoutFilter@43076326,
org.springframework.security.web.savedrequest.RequestCacheAwareFilter@6479b4cb,
org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@4225b4e2,
org.springframework.security.web.authentication.AnonymousAuthenticationFilter@6272936b,
org.springframework.security.web.session.SessionManagementFilter@5a349644,
org.springframework.security.web.access.ExceptionTranslationFilter@4ac64dc2,
org.springframework.security.web.access.intercept.FilterSecurityInterceptor@5a4a8a33
]
通过日志记录可以发现在整个过程中
也可以通过断点调试的方法进行调试
过滤器父类
对于每一个过滤器,其最终都会直接或者间接的继承到这两个父类
- OncePerRequest
能够保证每一次请求只会通过一次Filter 不会重复执行
- GenericFilterBean
Filter接口的基本实现类
- 可以将web.xml中
标签中的【-init-param】配置项读取为bean的属性 - 可以作为任意Filter类的简单父类
- 其子类可以任意自定义自己所需要的属性
- 将实际的工作交给子类实现,其子类必须实现doFIlter方法
- 不依赖SpringApplicationContext,不会读取其容器信息,而是通过读取Spring Root Application 中的service bean进行获取,通常是通过Filter中的getServletContext方法进行获取
- 可以将web.xml中
过滤链解析
WebAsyncManagerIntegrationFilter
public final class WebAsyncManagerIntegrationFilter extends OncePerRequestFilter {
private static final Object CALLABLE_INTERCEPTOR_KEY = new Object();
public WebAsyncManagerIntegrationFilter() {
}
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
SecurityContextCallableProcessingInterceptor securityProcessingInterceptor = (SecurityContextCallableProcessingInterceptor)asyncManager.getCallableInterceptor(CALLABLE_INTERCEPTOR_KEY);
if (securityProcessingInterceptor == null) {
asyncManager.registerCallableInterceptor(CALLABLE_INTERCEPTOR_KEY, new SecurityContextCallableProcessingInterceptor());
}
filterChain.doFilter(request, response);
}
}
- 根据请求封装获取WebAsyncManager
从WebAsyncManager获取/注册SecurityContextCallableProcessingInterceptor
SecurityContextPersistenceFilter
public class SecurityContextPersistenceFilter extends GenericFilterBean { static final String FILTER_APPLIED = "__spring_security_scpf_applied"; private SecurityContextRepository repo; private boolean forceEagerSessionCreation; public SecurityContextPersistenceFilter() { this(new HttpSessionSecurityContextRepository()); } public SecurityContextPersistenceFilter(SecurityContextRepository repo) { this.forceEagerSessionCreation = false; this.repo = repo; } public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { this.doFilter((HttpServletRequest)request, (HttpServletResponse)response, chain); } private void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException { if (request.getAttribute("__spring_security_scpf_applied") != null) { chain.doFilter(request, response); } else { request.setAttribute("__spring_security_scpf_applied", Boolean.TRUE); if (this.forceEagerSessionCreation) { HttpSession session = request.getSession(); if (this.logger.isDebugEnabled() && session.isNew()) { this.logger.debug(LogMessage.format("Created session %s eagerly", session.getId())); } } HttpRequestResponseHolder holder = new HttpRequestResponseHolder(request, response); SecurityContext contextBeforeChainExecution = this.repo.loadContext(holder); boolean var10 = false; try { var10 = true; SecurityContextHolder.setContext(contextBeforeChainExecution); if (contextBeforeChainExecution.getAuthentication() == null) { this.logger.debug("Set SecurityContextHolder to empty SecurityContext"); } else if (this.logger.isDebugEnabled()) { this.logger.debug(LogMessage.format("Set SecurityContextHolder to %s", contextBeforeChainExecution)); } chain.doFilter(holder.getRequest(), holder.getResponse()); var10 = false; } finally { if (var10) { SecurityContext contextAfterChainExecution = SecurityContextHolder.getContext(); SecurityContextHolder.clearContext(); this.repo.saveContext(contextAfterChainExecution, holder.getRequest(), holder.getResponse()); request.removeAttribute("__spring_security_scpf_applied"); this.logger.debug("Cleared SecurityContextHolder to complete request"); } } SecurityContext contextAfterChainExecution = SecurityContextHolder.getContext(); SecurityContextHolder.clearContext(); this.repo.saveContext(contextAfterChainExecution, holder.getRequest(), holder.getResponse()); request.removeAttribute("__spring_security_scpf_applied"); this.logger.debug("Cleared SecurityContextHolder to complete request"); } } ………… }
先实例SecurityContextHolder->HttpSessionSecurityContextRepository(下面以repo代替)作用:其会从Session中取出已认证用户的信息,提高效率,避免每一次请求都要查询用户认证信息。
- 根据请求和响应构建HttpRequestResponseHolder
- repo根据HttpRequestResponseHolder加载context获取SecurityContext
- SecurityContextHolder将获得到的SecurityContext设置到Context中,然后继续向下执行其他过滤器
- finally-> SecurityContextHolder获取SecurityContext,然后清除,并将其和请求信息保存到repo,从请求中移除FILTER_APPLIED属性
HeaderWriterFilter
```java
public class HeaderWriterFilter extends OncePerRequestFilter {
private final List
public HeaderWriterFilter(List<HeaderWriter> headerWriters) {
Assert.notEmpty(headerWriters, "headerWriters cannot be null or empty");
this.headerWriters = headerWriters;
}
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
if (this.shouldWriteHeadersEagerly) {
this.doHeadersBefore(request, response, filterChain);
} else {
this.doHeadersAfter(request, response, filterChain);
}
}
private void doHeadersBefore(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws IOException, ServletException {
this.writeHeaders(request, response);
filterChain.doFilter(request, response);
}
private void doHeadersAfter(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws IOException, ServletException {
HeaderWriterFilter.HeaderWriterResponse headerWriterResponse = new HeaderWriterFilter.HeaderWriterResponse(request, response);
HeaderWriterFilter.HeaderWriterRequest headerWriterRequest = new HeaderWriterFilter.HeaderWriterRequest(request, headerWriterResponse);
try {
filterChain.doFilter(headerWriterRequest, headerWriterResponse);
} finally {
headerWriterResponse.writeHeaders();
}
}
void writeHeaders(HttpServletRequest request, HttpServletResponse response) {
Iterator var3 = this.headerWriters.iterator();
while(var3.hasNext()) {
HeaderWriter writer = (HeaderWriter)var3.next();
writer.writeHeaders(request, response);
}
}
……………… } ```
LogoutFilter
- 匹配URL,默认为/logout
-
RequestCacheAwareFilter
通过HttpSessionRequestCache内部维护了一个RequestCache,用于缓存HttpServletRequest
SecurityContextHolderAwareRequestFilter
通过HttpSessionRequestCache内部维护了一个RequestCache,用于缓存HttpServletRequest
AnonymousAuthenticationFilter
匿名身份过滤器,这个过滤器个人认为很重要,需要将它与UsernamePasswordAuthenticationFilter 放在一起比较理解,spring security为了兼容未登录的访问,也走了一套认证流程,只不过是一个匿名的身份。
- 当SecurityContextHolder中认证信息为空,则会创建一个匿名用户存入到SecurityContextHolder中。
- 匿名认证过滤器,Anonymous匿名身份是Spirng Security为了整体逻辑的统一性,即使是未通过认证的用户,也给予了一个匿名身份。而AnonymousAuthenticationFilter该过滤器的位置也是非常的科学的,它位于常用的身份认证过滤器
(如UsernamePasswordAuthenticationFilter、BasicAuthenticationFilter、RememberMeAuthenticationFilter)之后,
意味着只有在上述身份过滤器执行完毕后,SecurityContext依旧没有用户信息,AnonymousAuthenticationFilter该过滤器才会有意义—-给予用户一个匿名身份。
SessionManagementFilter
- securityContextRepository限制同一用户开启多个会话的数量
- SessionAuthenticationStrategy防止session-fixation protection attack(保护非匿名用户)
ExceptionTranslationFilter
ExceptionTranslationFilter异常转换过滤器位于整个springSecurityFilterChain的后方,用来转换整个链路中出现的异常
此过滤器的作用是处理FilterSecurityInterceptor中抛出的异常,然后将请求重定向到对应页面,或返回对应的响应错误代码
FilterSecurityInterceptor
- 获取到所配置资源访问的授权信息
- 根据SecurityContextHolder中存储的用户信息来决定其是否有权限
- 主要一些实现功能在其父类AbstractSecurityInterceptor中