zoukankan      html  css  js  c++  java
  • 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));
                    }
                }
            }
        }
  • 相关阅读:
    wepy根据下标对数组中的某个对象的元素进行赋值
    wepy中的this.$apply()在什么时候使用
    wepy的安装与卸载
    vue-cli4.0更新后怎样将eslint关闭
    vue报错error 'projectName' is defined but never used no-unused-vars
    js数组对象去重(同时判断对象中的每一个属性,若其对应的属性值都相同,则去重)
    数字金额变为大写
    通过navigator.userAgent判断浏览器类型
    js获取iframe中的元素以及在iframe中获取父级的元素(包括iframe中不存在name和id的情况)
    html转成pdf,下载(html2canvas 和 jsPDF)
  • 原文地址:https://www.cnblogs.com/LQBlog/p/15529767.html
Copyright © 2011-2022 走看看