Spring Security Web 5.1.2 源码解析 — HttpSessionRequestCache

扫码关注公众号:Java 技术驿站

发送:vip
将链接复制到本浏览器,永久解锁本站全部文章

【公众号:Java 技术驿站】 【加作者微信交流技术,拉技术群】
免费领取10G资料包与项目实战视频资料

概述

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

来源:http://ddrv.cn/a/88268

赞(1) 打赏
版权归原创作者所有,任何形式的转载请联系博主:daming_90:Java 技术驿站 » Spring Security Web 5.1.2 源码解析 — HttpSessionRequestCache

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏