前言:
在熟悉Spring Security的使用和基本操作后,有时根据项目需求,我们需要在security原有的过滤器链中,添加符合我们自己的过滤器来实现功能时,我们就必须得先了解security的核心过滤链的流程和每个过滤器的各自功能,以此,我们才可以在特点的过滤器前后加入属于我们项目需求的过滤器。
在配置了spring security了之后,会在运行项目的时候,DefaultSecurityFilterChain会输出相关log:
public DefaultSecurityFilterChain(RequestMatcher requestMatcher, List<Filter> filters){ logger.info("Creating filter chain: " + requestMatcher + ", " + filters); this.requestMatcher = requestMatcher; this.filters = new ArrayList<Filter>(filters); }
[main] o.s.s.web.DefaultSecurityFilterChain : Creating filter chain: org.springframework.security.web.util.matcher.AnyRequestMatcher@1, [ org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@184de357, org.springframework.security.web.context.SecurityContextPersistenceFilter@521ba38f, org.springframework.security.web.header.HeaderWriterFilter@77bb916f, org.springframework.security.web.csrf.CsrfFilter@76b305e1, org.springframework.security.web.authentication.logout.LogoutFilter@17c53dfb, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@2086d469, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@b1d19ff, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@efe49ab, org.springframework.security.web.session.SessionManagementFilter@5a48d186, org.springframework.security.web.access.ExceptionTranslationFilter@273aaab7 ]
也可以从Debug进行查看:
在解析前,先说说两个至关重要的类:OncePerRequestFilter和GenericFilterBean,在过滤器链的过滤器中,或多或少间接或直接继承到
public final class WebAsyncManagerIntegrationFilter extends OncePerRequestFilter { ...... @Override 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); } }
从源码中,我们可以分析出WebAsyncManagerIntegrationFilter相关功能:
public class SecurityContextPersistenceFilter extends GenericFilterBean { ...... public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) res; if (request.getAttribute(FILTER_APPLIED) != null) { // ensure that filter is only applied once per request chain.doFilter(request, response); return; } final boolean debug = logger.isDebugEnabled(); request.setAttribute(FILTER_APPLIED, Boolean.TRUE); if (forceEagerSessionCreation) { HttpSession session = request.getSession(); if (debug && session.isNew()) { logger.debug("Eagerly created session: " + session.getId()); } } HttpRequestResponseHolder holder = new HttpRequestResponseHolder(request, response); SecurityContext contextBeforeChainExecution = repo.loadContext(holder); try { SecurityContextHolder.setContext(contextBeforeChainExecution); chain.doFilter(holder.getRequest(), holder.getResponse()); } finally { SecurityContext contextAfterChainExecution = SecurityContextHolder .getContext(); // Crucial removal of SecurityContextHolder contents - do this before anything // else. SecurityContextHolder.clearContext(); repo.saveContext(contextAfterChainExecution, holder.getRequest(), holder.getResponse()); request.removeAttribute(FILTER_APPLIED); if (debug) { logger.debug("SecurityContextHolder now cleared, as request processing completed"); } } } ...... }
从源码中,我们可以分析出SecurityContextPersistenceFilter相关功能:
public class HeaderWriterFilter extends OncePerRequestFilter { ...... @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { for (HeaderWriter headerWriter : headerWriters) { headerWriter.writeHeaders(request, response); } filterChain.doFilter(request, response); } }
从源码中,我们可以分析HeaderWriterFilter相关功能:
public final class CsrfFilter extends OncePerRequestFilter { ...... @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { request.setAttribute(HttpServletResponse.class.getName(), response); CsrfToken csrfToken = this.tokenRepository.loadToken(request); final boolean missingToken = csrfToken == null; if (missingToken) { csrfToken = this.tokenRepository.generateToken(request); this.tokenRepository.saveToken(csrfToken, request, response); } request.setAttribute(CsrfToken.class.getName(), csrfToken); request.setAttribute(csrfToken.getParameterName(), csrfToken); if (!this.requireCsrfProtectionMatcher.matches(request)) { filterChain.doFilter(request, response); return; } String actualToken = request.getHeader(csrfToken.getHeaderName()); if (actualToken == null) { actualToken = request.getParameter(csrfToken.getParameterName()); } if (!csrfToken.getToken().equals(actualToken)) { if (this.logger.isDebugEnabled()) { this.logger.debug("Invalid CSRF token found for " + UrlUtils.buildFullRequestUrl(request)); } if (missingToken) { this.accessDeniedHandler.handle(request, response, new MissingCsrfTokenException(actualToken)); } else { this.accessDeniedHandler.handle(request, response, new InvalidCsrfTokenException(csrfToken, actualToken)); } return; } filterChain.doFilter(request, response); } ...... }
从源码中,我们可以分析出CsrfFilter相关功能:
public class LogoutFilter extends GenericFilterBean { ...... public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) res; if (requiresLogout(request, response)) { Authentication auth = SecurityContextHolder.getContext().getAuthentication(); if (logger.isDebugEnabled()) { logger.debug("Logging out user '" + auth + "' and transferring to logout destination"); } this.handler.logout(request, response, auth); logoutSuccessHandler.onLogoutSuccess(request, response, auth); return; } chain.doFilter(request, response); } ...... }
从源码中,我们可以分析出LogoutFilter相关功能:
public class RequestCacheAwareFilter extends GenericFilterBean { ...... public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest wrappedSavedRequest = requestCache.getMatchingRequest( (HttpServletRequest) request, (HttpServletResponse) response); chain.doFilter(wrappedSavedRequest == null ? request : wrappedSavedRequest, response); } }
从源码中,我们可以分析出RequestCacheAwareFilter相关功能:
public class SecurityContextHolderAwareRequestFilter extends GenericFilterBean { ...... public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { chain.doFilter(this.requestFactory.create((HttpServletRequest) req, (HttpServletResponse) res), res); } ...... }
从源码中,我们可以分析出SecurityContextHolderAwareRequestFilter相关功能:
public class AnonymousAuthenticationFilter extends GenericFilterBean implements InitializingBean { ...... public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { if (SecurityContextHolder.getContext().getAuthentication() == null) { SecurityContextHolder.getContext().setAuthentication( createAuthentication((HttpServletRequest) req)); if (logger.isDebugEnabled()) { logger.debug("Populated SecurityContextHolder with anonymous token: '" + SecurityContextHolder.getContext().getAuthentication() + "'"); } } else { if (logger.isDebugEnabled()) { logger.debug("SecurityContextHolder not populated with anonymous token, as it already contained: '" + SecurityContextHolder.getContext().getAuthentication() + "'"); } } chain.doFilter(req, res); } ...... }
从源码中,我们可以分析出AnonymousAuthenticationFilter相关功能:
public class SessionManagementFilter extends GenericFilterBean { ...... public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) res; if (request.getAttribute(FILTER_APPLIED) != null) { chain.doFilter(request, response); return; } request.setAttribute(FILTER_APPLIED, Boolean.TRUE); if (!securityContextRepository.containsContext(request)) { Authentication authentication = SecurityContextHolder.getContext() .getAuthentication(); if (authentication != null && !trustResolver.isAnonymous(authentication)) { // The user has been authenticated during the current request, so call the // session strategy try { sessionAuthenticationStrategy.onAuthentication(authentication, request, response); } catch (SessionAuthenticationException e) { // The session strategy can reject the authentication logger.debug( "SessionAuthenticationStrategy rejected the authentication object", e); SecurityContextHolder.clearContext(); failureHandler.onAuthenticationFailure(request, response, e); return; } // Eagerly save the security context to make it available for any possible // re-entrant // requests which may occur before the current request completes. // SEC-1396. securityContextRepository.saveContext(SecurityContextHolder.getContext(), request, response); } else { // No security context or authentication present. Check for a session // timeout if (request.getRequestedSessionId() != null && !request.isRequestedSessionIdValid()) { if (logger.isDebugEnabled()) { logger.debug("Requested session ID " + request.getRequestedSessionId() + " is invalid."); } if (invalidSessionStrategy != null) { invalidSessionStrategy .onInvalidSessionDetected(request, response); return; } } } } chain.doFilter(request, response); } ...... }
从源码中,我们可以分析出SessionManagementFilter相关功能:
public class ExceptionTranslationFilter extends GenericFilterBean { ...... public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) req; HttpServletResponse response = (HttpServletResponse) res; try { chain.doFilter(request, response); logger.debug("Chain processed normally"); } catch (IOException ex) { throw ex; } catch (Exception ex) { // Try to extract a SpringSecurityException from the stacktrace Throwable[] causeChain = throwableAnalyzer.determineCauseChain(ex); RuntimeException ase = (AuthenticationException) throwableAnalyzer .getFirstThrowableOfType(AuthenticationException.class, causeChain); if (ase == null) { ase = (AccessDeniedException) throwableAnalyzer.getFirstThrowableOfType( AccessDeniedException.class, causeChain); } if (ase != null) { handleSpringSecurityException(request, response, chain, ase); } else { // Rethrow ServletExceptions and RuntimeExceptions as-is if (ex instanceof ServletException) { throw (ServletException) ex; } else if (ex instanceof RuntimeException) { throw (RuntimeException) ex; } // Wrap other Exceptions. This shouldn't actually happen // as we've already covered all the possibilities for doFilter throw new RuntimeException(ex); } } } ...... }
从源码中,我们可以分析出ExceptionTranslationFilter相关功能:
public class FilterSecurityInterceptor extends AbstractSecurityInterceptor implements Filter { ...... public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { FilterInvocation fi = new FilterInvocation(request, response, chain); invoke(fi); } public FilterInvocationSecurityMetadataSource getSecurityMetadataSource() { return this.securityMetadataSource; } public SecurityMetadataSource obtainSecurityMetadataSource() { return this.securityMetadataSource; } public void invoke(FilterInvocation fi) throws IOException, ServletException { if ((fi.getRequest() != null) && (fi.getRequest().getAttribute(FILTER_APPLIED) != null) && observeOncePerRequest) { // filter already applied to this request and user wants us to observe // once-per-request handling, so don't re-do security checking fi.getChain().doFilter(fi.getRequest(), fi.getResponse()); } else { // first time this request being called, so perform security checking if (fi.getRequest() != null) { fi.getRequest().setAttribute(FILTER_APPLIED, Boolean.TRUE); } InterceptorStatusToken token = super.beforeInvocation(fi); try { fi.getChain().doFilter(fi.getRequest(), fi.getResponse()); } finally { super.finallyInvocation(token); } super.afterInvocation(token, null); } } ...... }
从源码中,我们可以分析出FilterSecurityInterceptor相关功能:
public class UsernamePasswordAuthenticationFilter extends AbstractAuthenticationProcessingFilter { ...... public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException { if (postOnly && !request.getMethod().equals("POST")) { throw new AuthenticationServiceException( "Authentication method not supported: " + request.getMethod()); } String username = obtainUsername(request); String password = obtainPassword(request); if (username == null) { username = ""; } if (password == null) { password = ""; } username = username.trim(); UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken( username, password); // Allow subclasses to set the "details" property setDetails(request, authRequest); return this.getAuthenticationManager().authenticate(authRequest); } ...... }
从源码中,我们可以分析出UsernamePasswordAuthenticationFilter相关功能: