Springsecurity源码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));
                }
            }
        }
    }
原文地址:https://www.cnblogs.com/LQBlog/p/15529767.html