Spring Security Web 5.1.2 原始碼解析 -- HttpSessionRequestCache
阿新 • • 發佈:2018-12-12
概述
Spring Security Web
認證機制(通常指表單登入)中登入成功後頁面需要跳轉到原來客戶請求的URL。該過程中首先需要將原來的客戶請求快取下來,然後登入成功後將快取的請求從快取中提取出來。
針對該需求,Spring Security Web
提供了在http session
中快取請求的能力,也就是HttpSessionRequestCache
。HttpSessionRequestCache
所儲存的請求必須封裝成一個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;
}
}