spring security 4 filter SecurityContextPersistenceFilter(三)

今天我們講的filter是SecurityContextPersistenceFilter,通過其名字,就能大概猜出來這個過濾器的作用,就是用來持久化SecurityContext實例用的,也是spring security filter 核心的過濾器之一。

      接下去我們將根據其源碼分析一下其作用,先看看doFilter這個方法

public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest)req;
        HttpServletResponse response = (HttpServletResponse)res;
        if (request.getAttribute("__spring_security_scpf_applied") != null) {
            chain.doFilter(request, response);
        } else {
            boolean debug = this.logger.isDebugEnabled();
            request.setAttribute("__spring_security_scpf_applied", Boolean.TRUE);
            if (this.forceEagerSessionCreation) {
                HttpSession session = request.getSession();
                if (debug && session.isNew()) {
                    this.logger.debug("Eagerly created session: " + session.getId());
                }
            }
         // 包裝 request ,response
            HttpRequestResponseHolder holder = new HttpRequestResponseHolder(request, response);
          // 從session中獲取安全上下文信息
            SecurityContext contextBeforeChainExecution = this.repo.loadContext(holder);
            boolean var13 = false;

            try {
 // //請求開始時,設置安全上下文信息,這樣就避免了用戶直接從Session中獲取安全上下文信息
                var13 = true;
                SecurityContextHolder.setContext(contextBeforeChainExecution);
                chain.doFilter(holder.getRequest(), holder.getResponse());
                var13 = false;
            } finally {
        // 過濾器走完之後就清空SecurityContextHolder中的SecurityContext 對象
                if (var13) {
                    SecurityContext contextAfterChainExecution = SecurityContextHolder.getContext();
                    SecurityContextHolder.clearContext();
                    this.repo.saveContext(contextAfterChainExecution, holder.getRequest(), holder.getResponse());
                    request.removeAttribute("__spring_security_scpf_applied");
                    if (debug) {
                        this.logger.debug("SecurityContextHolder now cleared, as request processing completed");
                    }

                }
            }

            SecurityContext contextAfterChainExecution = SecurityContextHolder.getContext();
            SecurityContextHolder.clearContext();
            this.repo.saveContext(contextAfterChainExecution, holder.getRequest(), holder.getResponse());
            request.removeAttribute("__spring_security_scpf_applied");
            if (debug) {
                this.logger.debug("SecurityContextHolder now cleared, as request processing completed");
            }

        }
    }

我們先來看一下核心代碼

 // 將request,response對象封裝到  HttpRequestResponseHolder 裏
 HttpRequestResponseHolder holder = new HttpRequestResponseHolder(request, response);
// 從SecurityContextRepository對象中獲取SecurityContext 
            SecurityContext contextBeforeChainExecution = this.repo.loadContext(holder);

然後我們看一下SecurityContextRepository接口的實現類HttpSessionSecurityContextRepository的loadContext方法
 


    public SecurityContext loadContext(HttpRequestResponseHolder requestResponseHolder) {
        HttpServletRequest request = requestResponseHolder.getRequest();
        HttpServletResponse response = requestResponseHolder.getResponse();
        HttpSession httpSession = request.getSession(false);
   //  根據特定的key從session中獲取SecurityContext
        SecurityContext context = this.readSecurityContextFromSession(httpSession);
        if (context == null) {
            if (this.logger.isDebugEnabled()) {
                this.logger.debug("No SecurityContext was available from the HttpSession: " + httpSession + ". A new one will be created.");
            }
         //  如果session裏面沒有就重新生成一個SecurityContext 

            context = this.generateNewContext();
        }

        
        HttpSessionSecurityContextRepository.SaveToSessionResponseWrapper wrappedResponse = new HttpSessionSecurityContextRepository.SaveToSessionResponseWrapper(response, request, httpSession != null, context);
        requestResponseHolder.setResponse(wrappedResponse);
        if (this.isServlet3) {
            requestResponseHolder.setRequest(new HttpSessionSecurityContextRepository.Servlet3SaveToSessionRequestWrapper(request, wrappedResponse));
        }

        return context;
    }

然後我們先來看看這個方法裏面的readSecurityContextFromSession方法

private SecurityContext readSecurityContextFromSession(HttpSession httpSession) {
        boolean debug = this.logger.isDebugEnabled();
        if (httpSession == null) {
            if (debug) {
                this.logger.debug("No HttpSession currently exists");
            }

            return null;
        } else {
            Object contextFromSession = httpSession.getAttribute(this.springSecurityContextKey);
            if (contextFromSession == null) {
                if (debug) {
                    this.logger.debug("HttpSession returned null object for SPRING_SECURITY_CONTEXT");
                }

                return null;
            } else if (!(contextFromSession instanceof SecurityContext)) {
                if (this.logger.isWarnEnabled()) {
                    this.logger.warn(this.springSecurityContextKey + " did not contain a SecurityContext but contained: '" + contextFromSession + "'; are you improperly modifying the HttpSession directly (you should always use SecurityContextHolder) or using the HttpSession attribute reserved for this class?");
                }

                return null;
            } else {
                if (debug) {
                    this.logger.debug("Obtained a valid SecurityContext from " + this.springSecurityContextKey + ": '" + contextFromSession + "'");
                }

                return (SecurityContext)contextFromSession;
            }
        }
    }

從代碼上看這個方法就是從session中根據springSecurityContextKey獲取SecurityContext對象,沒有就直接new一個,然後set到SecurityContextHolder裏面,請求執行完了之後調用SecurityContextHolder.clearContext()清除

總結:

請求來臨時,創建SecurityContext放到SecurityContextHolder裏,請求結束時清空SecurityContextHolder的SecurityContext,避免每回從session裏面取

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章