1. 程式人生 > >Spring Security Web 5.1.2 原始碼解析 -- HttpSessionRequestCache

Spring Security Web 5.1.2 原始碼解析 -- HttpSessionRequestCache

概述

Spring Security Web認證機制(通常指表單登入)中登入成功後頁面需要跳轉到原來客戶請求的URL。該過程中首先需要將原來的客戶請求快取下來,然後登入成功後將快取的請求從快取中提取出來。

針對該需求,Spring Security Web 提供了在http session中快取請求的能力,也就是HttpSessionRequestCacheHttpSessionRequestCache所儲存的請求必須封裝成一個SavedRequest介面物件,實際上,HttpSessionRequestCache總是使用自己的SavedRequest預設實現DefaultSavedRequest

原始碼解析

package org.springframework.security.web.savedrequest;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.
security.web.PortResolver; import org.springframework.security.web.PortResolverImpl; import org.springframework.security.web.util.UrlUtils; import org.springframework.security.web.util.matcher.AnyRequestMatcher; import org.springframework.security.web.util.matcher.RequestMatcher; /** * RequestCache which stores the SavedRequest in the HttpSession. * 將SavedRequest儲存到HttpSession中的RequestCache。 * * The DefaultSavedRequest class is used as the implementation. * 這裡使用的SavedRequest是其預設實現DefaultSavedRequest。 * * @author Luke Taylor * @author Eddú Meléndez * @since 3.0 */
public class HttpSessionRequestCache implements RequestCache { // 將請求快取到session時預設使用的session屬性名稱 static final String SAVED_REQUEST = "SPRING_SECURITY_SAVED_REQUEST"; protected final Log logger = LogFactory.getLog(this.getClass()); // 用於解析請求中的 server:port 資訊 private PortResolver portResolver = new PortResolverImpl(); // 如果session不存在是否允許建立,預設為true可以建立 private boolean createSessionAllowed = true; // 用於判斷哪些請求可以被快取的請求匹配器,預設為任何請求都可以被快取, // 實際上會被外部指定覆蓋成: // 1. 必須是 GET /** // 2. 並且不能是 /**/favicon.* // 3. 並且不能是 application.json // 4. 並且不能是 XMLHttpRequest (也就是一般意義上的 ajax 請求) private RequestMatcher requestMatcher = AnyRequestMatcher.INSTANCE; // 將請求快取到session時使用的session屬性名稱,初始化為使用SAVED_REQUEST private String sessionAttrName = SAVED_REQUEST; /** * Stores the current request, provided the configuration properties allow it. * 在配置屬性requestMatcher匹配的情況下快取當前請求 */ public void saveRequest(HttpServletRequest request, HttpServletResponse response) { if (requestMatcher.matches(request)) { // 在配置屬性requestMatcher匹配的情況下快取當前請求, // 首先將當前請求包裝成一個DefaultSavedRequest,也就是從當前請求中獲取 // 各種必要的資訊組裝成一個DefaultSavedRequest DefaultSavedRequest savedRequest = new DefaultSavedRequest(request, portResolver); // 獲取session並執行快取動作,也就是將上面建立的DefaultSavedRequest物件 // 新增為session的一個名稱為this.sessionAttrName的屬性 if (createSessionAllowed || request.getSession(false) != null) { // Store the HTTP request itself. Used by // AbstractAuthenticationProcessingFilter // for redirection after successful authentication (SEC-29) request.getSession().setAttribute(this.sessionAttrName, savedRequest); logger.debug("DefaultSavedRequest added to Session: " + savedRequest); } } else { logger.debug("Request not saved as configured RequestMatcher did not match"); } } // 從session中提取所快取的請求物件,也就是獲取session中名稱為this.sessionAttrName的屬性, // 如果 session 不存在直接返回 null public SavedRequest getRequest(HttpServletRequest currentRequest, HttpServletResponse response) { HttpSession session = currentRequest.getSession(false); if (session != null) { return (SavedRequest) session.getAttribute(this.sessionAttrName); } return null; } // 從 session 中刪除所快取的請求物件,也就是移除session中名稱為this.sessionAttrName的屬性 public void removeRequest(HttpServletRequest currentRequest, HttpServletResponse response) { HttpSession session = currentRequest.getSession(false); if (session != null) { logger.debug("Removing DefaultSavedRequest from session if present"); session.removeAttribute(this.sessionAttrName); } } // 從 session 獲取快取的請求物件,檢驗它和當前請求是否一致,如果一致的話將其封裝成 // 一個SavedRequestAwareWrapper返回,同時刪除所快取的請求。其他情況則不做任何修改 // 動作,直接返回null。 public HttpServletRequest getMatchingRequest(HttpServletRequest request, HttpServletResponse response) { // 從 session 獲取快取的請求物件 SavedRequest saved = getRequest(request, response); if (!matchesSavedRequest(request, saved)) { // 如果快取的請求和當前請求不匹配則返回null logger.debug("saved request doesn't match"); return null; } // 如果快取的請求和當前請求匹配則刪除快取中快取的請求物件 removeRequest(request, response); // 封裝和返回從快取中提取到的請求物件 return new SavedRequestAwareWrapper(saved, request); } // 檢測當前請求和引數savedRequest是否匹配 private boolean matchesSavedRequest(HttpServletRequest request, SavedRequest savedRequest) { if (savedRequest == null) { return false; } if (savedRequest instanceof DefaultSavedRequest) { // 如果savedRequest是一個DefaultSavedRequest,則使用DefaultSavedRequest的 // 方法doesRequestMatch檢驗是否匹配 DefaultSavedRequest defaultSavedRequest = (DefaultSavedRequest) savedRequest; return defaultSavedRequest.doesRequestMatch(request, this.portResolver); } // 如果savedRequest不是一個DefaultSavedRequest,則通過比較二者的url是否相等 // 來檢驗二者是否匹配 String currentUrl = UrlUtils.buildFullRequestUrl(request); return savedRequest.getRedirectUrl().equals(currentUrl); } /** * Allows selective use of saved requests for a subset of requests. By default any * request will be cached by the saveRequest method. * * If set, only matching requests will be cached. * * 指定哪些請求會被快取,如果不指定,預設情況是所有請求都會被快取 * @param requestMatcher a request matching strategy which defines which requests * should be cached. */ public void setRequestMatcher(RequestMatcher requestMatcher) { this.requestMatcher = requestMatcher; } /** * If true, indicates that it is permitted to store the target URL and * exception information in a new HttpSession (the default). In * situations where you do not wish to unnecessarily create HttpSessions * - because the user agent will know the failed URL, such as with BASIC or Digest * authentication - you may wish to set this property to false. */ public void setCreateSessionAllowed(boolean createSessionAllowed) { this.createSessionAllowed = createSessionAllowed; } public void setPortResolver(PortResolver portResolver) { this.portResolver = portResolver; } /** * If the sessionAttrName property is set, the request is stored in * the session using this attribute name. Default is * "SPRING_SECURITY_SAVED_REQUEST". * * @param sessionAttrName a new session attribute name. * @since 4.2.1 */ public void setSessionAttrName(String sessionAttrName) { this.sessionAttrName = sessionAttrName; } }