当前位置: 首页 > article >正文

SpringSession源码分析

默认对常规Session的理解和使用,如何使用Set-Cookie。

Maven库

常见的spring-session-data-redis依赖spring-session-core

    <dependency>
      <groupId>org.springframework.session</groupId>
      <artifactId>spring-session-core</artifactId>
    </dependency>

带着问题看源码

1、Controller代码块内request.getSession()是在哪创建的呢?
2、响应头Set-Cookie是在什么地方赋值的呢?

SessionRepositoryFilter 拦截器

org.springframework.session.web.http.SessionRepositoryFilter

    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        request.setAttribute(SESSION_REPOSITORY_ATTR, this.sessionRepository);
        // 请求包装,进入包装类可以看到request.getSession()源码
        SessionRepositoryFilter<S>.SessionRepositoryRequestWrapper wrappedRequest = new SessionRepositoryRequestWrapper(request, response);
        SessionRepositoryFilter<S>.SessionRepositoryResponseWrapper wrappedResponse = new SessionRepositoryResponseWrapper(wrappedRequest, response);

        try {
            filterChain.doFilter(wrappedRequest, wrappedResponse);
        } finally {
            // 响应头`Set-Cookie`赋值入口(这里不理解为啥使用请求提交,而不是用响应提交`wrappedResponse.commitSession()`)
            // 方法跳转到DefaultCookieSerializer类就看见具体Set-Cookie了
            wrappedRequest.commitSession();
        }

    }

SessionRepositoryRequestWrapper 包装类

org.springframework.session.web.http.SessionRepositoryFilter.SessionRepositoryRequestWrapper
Spring中存在很多继承HttpServletRequestWrapper的包装类

        public SessionRepositoryFilter<S>..SessionRepositoryRequestWrapper.HttpSessionWrapper getSession(boolean create) {
            SessionRepositoryFilter<S>..SessionRepositoryRequestWrapper.HttpSessionWrapper currentSession = this.getCurrentSession();
            if (currentSession != null) {
                return currentSession;
            } else {
                S requestedSession = this.getRequestedSession();
                if (requestedSession != null) {
                    if (this.getAttribute(SessionRepositoryFilter.INVALID_SESSION_ID_ATTR) == null) {
                        requestedSession.setLastAccessedTime(Instant.now());
                        this.requestedSessionIdValid = true;
                        currentSession = new HttpSessionWrapper(requestedSession, this.getServletContext());
                        currentSession.markNotNew();
                        this.setCurrentSession(currentSession);
                        return currentSession;
                    }
                } else {
                    if (SessionRepositoryFilter.SESSION_LOGGER.isDebugEnabled()) {
                        SessionRepositoryFilter.SESSION_LOGGER.debug("No session found by id: Caching result for getSession(false) for this HttpServletRequest.");
                    }

                    this.setAttribute(SessionRepositoryFilter.INVALID_SESSION_ID_ATTR, "true");
                }

                if (!create) {
                    return null;
                } else if (SessionRepositoryFilter.this.httpSessionIdResolver instanceof CookieHttpSessionIdResolver && this.response.isCommitted()) {
                    throw new IllegalStateException("Cannot create a session after the response has been committed");
                } else {
                    if (SessionRepositoryFilter.SESSION_LOGGER.isDebugEnabled()) {
                        SessionRepositoryFilter.SESSION_LOGGER.debug("A new session was created. To help you troubleshoot where the session was created we provided a StackTrace (this is not an error). You can prevent this from appearing by disabling DEBUG logging for " + SessionRepositoryFilter.SESSION_LOGGER_NAME, new RuntimeException("For debugging purposes only (not an error)"));
                    }

                    S session = SessionRepositoryFilter.this.sessionRepository.createSession();
                    session.setLastAccessedTime(Instant.now());
                    currentSession = new HttpSessionWrapper(session, this.getServletContext());
                    this.setCurrentSession(currentSession);
                    return currentSession;
                }
            }
        }
        // 看这里
        @Override
        public SessionRepositoryFilter<S>..SessionRepositoryRequestWrapper.HttpSessionWrapper getSession() {
            return this.getSession(true);
        }

DefaultCookieSerializer

org.springframework.session.web.http.DefaultCookieSerializer
特别注意地方是:Set-Cookie内值是通过Base64加密的。

    public void writeCookieValue(CookieSerializer.CookieValue cookieValue) {
        HttpServletRequest request = cookieValue.getRequest();
        HttpServletResponse response = cookieValue.getResponse();
        StringBuilder sb = new StringBuilder();
        sb.append(this.cookieName).append('=');
        String value = this.getValue(cookieValue);
        if (value != null && value.length() > 0) {
            this.validateValue(value);
            sb.append(value);
        }

        int maxAge = this.getMaxAge(cookieValue);
        if (maxAge > -1) {
            sb.append("; Max-Age=").append(cookieValue.getCookieMaxAge());
            ZonedDateTime expires = maxAge != 0 ? ZonedDateTime.now(this.clock).plusSeconds((long)maxAge) : Instant.EPOCH.atZone(ZoneOffset.UTC);
            sb.append("; Expires=").append(expires.format(DateTimeFormatter.RFC_1123_DATE_TIME));
        }

        String domain = this.getDomainName(request);
        if (domain != null && domain.length() > 0) {
            this.validateDomain(domain);
            sb.append("; Domain=").append(domain);
        }

        String path = this.getCookiePath(request);
        if (path != null && path.length() > 0) {
            this.validatePath(path);
            sb.append("; Path=").append(path);
        }

        if (this.isSecureCookie(request)) {
            sb.append("; Secure");
        }

        if (this.useHttpOnlyCookie) {
            sb.append("; HttpOnly");
        }

        if (this.sameSite != null) {
            sb.append("; SameSite=").append(this.sameSite);
        }

        response.addHeader("Set-Cookie", sb.toString());
    }

🔚

源码分析主要是找到入口,关键入口代码已经贴上了,剩下的自己打断点看吧。


http://www.kler.cn/a/379802.html

相关文章:

  • qt QMenuBar详解
  • OpenEuler 使用ffmpeg x11grab捕获屏幕流,rtsp推流,并用vlc播放
  • 1565412310
  • 如何对LabVIEW软件进行性能评估?
  • net core Autofac 替换默认的服务容器 DI,微软自动的容器 不支持命名选项的
  • Optimizing Medical Image Segmentation with Advanced Decoder
  • 水仙花求和
  • 使用 pytorch 运行预训练模型的框架
  • D58【python 接口自动化学习】- python基础之异常
  • 不需要复制粘贴,重复内容如何使用Mac快速完成输入
  • 初始JavaEE篇——多线程(5):生产者-消费者模型、阻塞队列
  • 【Mac】Screen Recorder by Omi Mac:Omi录屏专家
  • 从最小作用量原理推导牛顿三大定律
  • 相机硬触发
  • 小红书笔记详情API接口系列(概述到示例案例)
  • Mac上的免费压缩软件-FastZip使用体验实测
  • Vue3的router和Vuex的学习笔记整理
  • WPF使用Prism框架首页界面
  • 语言模型的采样方法
  • 企业培训考试系统源码
  • 深入理解Spring、SpringMVC、SpringBoot和Spring Cloud的区别与用法
  • 服务器数据恢复—RAID5阵列硬盘坏道掉线导致存储不可用的数据恢复案例
  • 【前端开发入门】JavaScript快速入门--数据操作
  • [vulnhub] DC:9
  • antdesignvue + AWS-S3实现Minio大文件分片上传
  • qt QPainter详解