Spring-security源码-Filter之SecurityContextPersistenceFilter(十一)
主要是在认证 Filter链执行之前 维护SecurityContextHolder 方便我们后续通过SecurityContextHolder.getContext()获取当前会话用户信息
通过SecurityContextConfigurer初始化 默认设置源码处:https://www.cnblogs.com/LQBlog/p/15508248.html#autoid-12-0-0
private void applyDefaultConfiguration(HttpSecurity http) throws Exception { //http本质也是build 这里都是配置默认的config configure add CsrfConfigurer http.csrf(); //默认增加一个WebAsyncManagerIntegrationFilter http.addFilter(new WebAsyncManagerIntegrationFilter()); //configures add ExceptionHandlingConfigurer http.exceptionHandling(); //configures add HeadersConfigurer http.headers(); //configures add SessionManagementConfigurer http.sessionManagement(); //configure add SecurityContextConfigurer http.securityContext(); //configure add RequestCacheConfigurer http.requestCache(); ///configure add AnonymousConfigurer http.anonymous(); ///configure add ServletApiConfigurer http.servletApi(); //configure DefaultLoginPageConfigurer http.apply(new DefaultLoginPageConfigurer<>()); //configure LogoutConfigurer http.logout(); }
SecurityContextPersistenceFilter
static final String FILTER_APPLIED = "__spring_security_scpf_applied"; private void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException { // 可以通过这个值设置忽略 if (request.getAttribute(FILTER_APPLIED) != null) { chain.doFilter(request, response); return; } request.setAttribute(FILTER_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())); } } //通过Holder封装request和response HttpRequestResponseHolder holder = new HttpRequestResponseHolder(request, response); //<1>SecurityContextRepository 加载SecurityContext SecurityContext contextBeforeChainExecution = this.repo.loadContext(holder); try { //set到ThreadLocal 后续我们可以通过SecurityContextHolder.getContext();获取当前登录信息 SecurityContextHolder.setContext(contextBeforeChainExecution); if (contextBeforeChainExecution.getAuthentication() == null) { logger.debug("Set SecurityContextHolder to empty SecurityContext"); } else { if (this.logger.isDebugEnabled()) { this.logger .debug(LogMessage.format("Set SecurityContextHolder to %s", contextBeforeChainExecution)); } } //执行后续filter chain.doFilter(holder.getRequest(), holder.getResponse()); } finally { //再次获取最新的Context 我们后续Filter可能更改Context SecurityContext contextAfterChainExecution = SecurityContextHolder.getContext(); // 清空ThreadLocal SecurityContextHolder.clearContext(); //<3>SecurityContextRepository 更新最新的Context this.repo.saveContext(contextAfterChainExecution, holder.getRequest(), holder.getResponse()); request.removeAttribute(FILTER_APPLIED); this.logger.debug("Cleared SecurityContextHolder to complete request"); } }
SecurityContextRepository
类图
默认提供三种实现
NullSecurityContextRepository 为空实现相当于什么都不用做
TestSecurityContextRepository 应该用于测试
HttpSessionSecurityContextRepository 通过session维护 我们主要看这个 默认也是这个。可以给我们很多启发 如何定制扩展
我们可以通过一下方式指定
http.securityContext().securityContextRepository({})
HttpSessionSecurityContextRepository
<1>
org.springframework.security.web.context.HttpSessionSecurityContextRepository#loadContext
public SecurityContext loadContext(HttpRequestResponseHolder requestResponseHolder) { //获得request HttpServletRequest request = requestResponseHolder.getRequest(); //获得response HttpServletResponse response = requestResponseHolder.getResponse(); //获得session 针对登录成功都维护了session HttpSession httpSession = request.getSession(false); //<2>从session读取Context SecurityContext context = readSecurityContextFromSession(httpSession); if (context == null) { context = generateNewContext(); if (this.logger.isTraceEnabled()) { this.logger.trace(LogMessage.format("Created %s", context)); } } //通过SaveToSessionResponseWrapper SaveToSessionRequestWrapper装饰 Request和Response SaveToSessionResponseWrapper wrappedResponse = new SaveToSessionResponseWrapper(response, request, httpSession != null, context); requestResponseHolder.setResponse(wrappedResponse); requestResponseHolder.setRequest(new SaveToSessionRequestWrapper(request, wrappedResponse)); return context; }
<2>
org.springframework.security.web.context.HttpSessionSecurityContextRepository#readSecurityContextFromSession
/** * @param httpSession the session obtained from the request. */ private SecurityContext readSecurityContextFromSession(HttpSession httpSession) { //如果session为空 表示未登录 直接返回null if (httpSession == null) { this.logger.trace("No HttpSession currently exists"); return null; } //从session获取保存在session的当前用户信息 默认是在"SPRING_SECURITY_CONTEXT"这个key Object contextFromSession = httpSession.getAttribute(this.springSecurityContextKey); //如果为null直接返回null if (contextFromSession == null) { if (this.logger.isTraceEnabled()) { this.logger.trace(LogMessage.format("Did not find SecurityContext in HttpSession %s " + "using the SPRING_SECURITY_CONTEXT session attribute", httpSession.getId())); } return null; } // 不是SecurityContext 实现类 则返回null if (!(contextFromSession instanceof SecurityContext)) { this.logger.warn(LogMessage.format( "%s did not contain a SecurityContext but contained: '%s'; are you improperly " + "modifying the HttpSession directly (you should always use SecurityContextHolder) " + "or using the HttpSession attribute reserved for this class?", this.springSecurityContextKey, contextFromSession)); return null; } // Everything OK. The only non-null return from this method. return (SecurityContext) contextFromSession; }
<3>
org.springframework.security.web.context.HttpSessionSecurityContextRepository#saveContext
@Override public void saveContext(SecurityContext context, HttpServletRequest request, HttpServletResponse response) { SaveContextOnUpdateOrErrorResponseWrapper responseWrapper = WebUtils.getNativeResponse(response, SaveContextOnUpdateOrErrorResponseWrapper.class); Assert.state(responseWrapper != null, () -> "Cannot invoke saveContext on response " + response + ". You must use the HttpRequestResponseHolder.response after invoking loadContext"); //<4>委托给responseWrapper responseWrapper.saveContext(context); }
<4>
org.springframework.security.web.context.HttpSessionSecurityContextRepository.SaveToSessionResponseWrapper#saveContext
@Override protected void saveContext(SecurityContext context) { //获取用户信息 final Authentication authentication = context.getAuthentication(); HttpSession httpSession = this.request.getSession(false); //获取session 保存用户信息的key String springSecurityContextKey = HttpSessionSecurityContextRepository.this.springSecurityContextKey; //如果没有表示被清空 比如被我们置空 则从session中删除 if (authentication == null || HttpSessionSecurityContextRepository.this.trustResolver.isAnonymous(authentication)) { if (httpSession != null && this.authBeforeExecution != null) { //从session中删除 httpSession.removeAttribute(springSecurityContextKey); this.isSaveContextInvoked = true; } if (this.logger.isDebugEnabled()) { if (authentication == null) { this.logger.debug("Did not store empty SecurityContext"); } else { this.logger.debug("Did not store anonymous SecurityContext"); } } return; } //获取当前会话httSession如果没有则创建一个 httpSession = (httpSession != null) ? httpSession : createNewSessionIfAllowed(context, authentication); // If HttpSession exists, store current SecurityContext but only if it has // actually changed in this thread (see SEC-37, SEC-1307, SEC-1528) if (httpSession != null) { // We may have a new session, so check also whether the context attribute // is set SEC-1561 if (contextChanged(context) || httpSession.getAttribute(springSecurityContextKey) == null) { //保存到session httpSession.setAttribute(springSecurityContextKey, context); this.isSaveContextInvoked = true; if (this.logger.isDebugEnabled()) { this.logger.debug(LogMessage.format("Stored %s to HttpSession [%s]", context, httpSession)); } } } }