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;
	}
}

你可能感兴趣的:(Spring,Security,分析)