前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >他来了,读取SpringSession中读取session的过程

他来了,读取SpringSession中读取session的过程

作者头像
码农飞哥
发布2021-08-18 10:45:16
9080
发布2021-08-18 10:45:16
举报
文章被收录于专栏:好好学习

前言

上一篇我们介绍了SpringSession中Session的保存过程,今天我们接着来看看Session的读取过程。相对保存过程,读取过程相对比较简单。 本文想从源码的角度,详细介绍一下Session的读取过程。

读取过程的时序图

在这里插入图片描述 如上,是读取Session的时序图,首先代码入口还是SessionRepositoryFilter过滤器的doFilterInternal方法。这个方法里还是会调用到SessionRepositoryRequestWrapper类的getSession()方法,这个getSession方法是读取Session的开始,这个方法内部会调用getSession(true)方法。那我们就从SessionRepositoryRequestWrapper类的getSession(true)方法开始说起。

getSession(true)方法。

代码语言:javascript
复制
@Override
        public HttpSessionWrapper getSession(boolean create) {
            //获取HttpSessionWrapper类,这个类会包装HttpSession
            HttpSessionWrapper currentSession = getCurrentSession();
            if (currentSession != null) {
                return currentSession;
            }
            //获取RedisSession
            S requestedSession = getRequestedSession();
            if (requestedSession != null) {
                if (getAttribute(INVALID_SESSION_ID_ATTR) == null) {
                    requestedSession.setLastAccessedTime(Instant.now());
                    this.requestedSessionIdValid = true;
                    currentSession = new HttpSessionWrapper(requestedSession, getServletContext());
                    currentSession.setNew(false);
                    setCurrentSession(currentSession);
                    return currentSession;
                }
            }
            //省略部分代码
    }

这个方法首先获取HttpSessionWrapper对象,这个对象的作用是用于封装session,返回给其上一层,如果可以获取到则说明Session信息已经拿到了,就直接返回。 如果获取不到则调用getRequestedSession()方法。这个方法就是获取session的主方法。接着让我们来看看这个方法吧。

getRequestedSession()方法

代码语言:javascript
复制
private S getRequestedSession() {
            if (!this.requestedSessionCached) {
                //从cookie中获取sessionid集合
                List<String> sessionIds = SessionRepositoryFilter.this.httpSessionIdResolver
                        .resolveSessionIds(this);
                //遍历sessionid集合,分别获取HttpSession
                for (String sessionId : sessionIds) {
                    if (this.requestedSessionId == null) {
                        this.requestedSessionId = sessionId;
                    }
                    //根据sessionid去redis中获取session
                    S session = SessionRepositoryFilter.this.sessionRepository
                            .findById(sessionId);
                    if (session != null) {
                        this.requestedSession = session;
                        this.requestedSessionId = sessionId;
                        break;
                    }
                }
                this.requestedSessionCached = true;
            }
            return this.requestedSession;
        }

如上,这个方法主要有两步:

  1. 从cookie中获取sessionid的集合,可能cookie中存在多个sessionid。
  2. 循环sessionid的集合,分别根据sessionid到redis中获取session。 获取sessionid是通过HttpSessionIdResolver接口的resolveSessionIds方法来实现的,SessionRepositoryFilter中定义了HttpSessionIdResolver接口的实例,其实现类是CookieHttpSessionIdResolver类。
代码语言:javascript
复制
    private HttpSessionIdResolver httpSessionIdResolver = new CookieHttpSessionIdResolver();

所以,SessionRepositoryFilter.this.httpSessionIdResolver的实例是一个CookieHttpSessionIdResolver对象。 而SessionRepositoryFilter.this.sessionRepository的实例是一个RedisOperationsSessionRepository对象。 那么接下来我们就分别来看看这个两个类的相关方法。

resolveSessionIds方法

接下来,我们就来到了CookieHttpSessionIdResolver类的resolveSessionIds方法,这个方法主要的作用就是从cookie中获取sessionid。

代码语言:javascript
复制
    @Override
    public List<String> resolveSessionIds(HttpServletRequest request) {
        return this.cookieSerializer.readCookieValues(request);
    }

看到这个方法之后,我们发现这个方法只是一个中转方法,内部直接把请求交给了readCookieValues方法。同样的在CookieHttpSessionIdResolver类内部也定义了cookieSerializer这个属性, 它的实例对象是DefaultCookieSerializer。所以,真正的操作逻辑还是在DefaultCookieSerializer类中完成的。

代码语言:javascript
复制
    private CookieSerializer cookieSerializer = new DefaultCookieSerializer();

接下来,我们就来看看DefaultCookieSerializer这个类的的readCookieValues方法。

readCookieValues方法

代码语言:javascript
复制
    @Override
    public List<String> readCookieValues(HttpServletRequest request) {
        //从请求头中获取cookies
        Cookie[] cookies = request.getCookies();
        List<String> matchingCookieValues = new ArrayList<>();
        if (cookies != null) {
            for (Cookie cookie : cookies) {
                //获取存放sessionid的那个cookie,cookieName默认是SESSION
                if (this.cookieName.equals(cookie.getName())) {
                    //默认的话sessionid是加密的
                    String sessionId = (this.useBase64Encoding
                            ? base64Decode(cookie.getValue())
                            : cookie.getValue());
                    if (sessionId == null) {
                        continue;
                    }
                    if (this.jvmRoute != null && sessionId.endsWith(this.jvmRoute)) {
                        sessionId = sessionId.substring(0,
                                sessionId.length() - this.jvmRoute.length());
                    }
                    matchingCookieValues.add(sessionId);
                }
            }
        }
        return matchingCookieValues;
    }

如上,这个从cookie中获取sessionid的方法也很简单,无非就是从当前的HttpServletRequest对象中获取所有的cookie,然后,提取name等于cookieName的cookie值。 这个cookie值就是sessionid。

findById方法

从cookie中那个sessionid之后会调用RedisOperationsSessionRepository类的findById方法,这个方法的作用就是从redis中获取保存的session信息。

代码语言:javascript
复制
        public RedisSession findById(String id) {
        //直接调用getSession方法
        return getSession(id, false);
    }


    private RedisSession getSession(String id, boolean allowExpired) {
        //获取当前session在redis保存的所有数据
        Map<Object, Object> entries = getSessionBoundHashOperations(id).entries();
        if (entries.isEmpty()) {
            return null;
        }
        //传入数据并组装成MapSession
        MapSession loaded = loadSession(id, entries);
        if (!allowExpired && loaded.isExpired()) {
            return null;
        }
        //将MapSession转成RedisSession,并最终返回
        RedisSession result = new RedisSession(loaded);
        result.originalLastAccessTime = loaded.getLastAccessedTime();
        return result;
    }

如上,我们可以看到findById方法内部直接调用了getSession方法,所以,所有的逻辑都在这个方法,而这个方法的逻辑分为三步:

  1. 根据sessionid获取当前session在redis保存的所有数据
  2. 传入数据并组装成MapSession
  3. 将MapSession转成RedisSession,并最终返回 我们一步步的看 首先,第一步根据sessionid获取当前session在redis保存的所有数据
代码语言:javascript
复制
    private BoundHashOperations<Object, Object, Object> getSessionBoundHashOperations(
            String sessionId) {
        //拿到key
        String key = getSessionKey(sessionId);
        //根据key获取值
        return this.sessionRedisOperations.boundHashOps(key);
    }
    //key是spring:session sessions:+sessionid
    String getSessionKey(String sessionId) {
        return this.namespace + "sessions:" + sessionId;
    }

需要注意的是,session保存到redis中的值不是字符类型的。而是通过对象保存的,是hash类型。

总结

至此,从Cookie中读取SessionId,然后,根据SessionId查询保存到Redis中的数据的全过程,希望对大家有所帮助。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-04-17,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 码农飞哥 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 读取过程的时序图
    • getSession(true)方法。
      • getRequestedSession()方法
        • resolveSessionIds方法
        • readCookieValues方法
          • findById方法
          • 总结
          相关产品与服务
          云数据库 Redis®
          腾讯云数据库 Redis®(TencentDB for Redis®)是腾讯云打造的兼容 Redis 协议的缓存和存储服务。丰富的数据结构能帮助您完成不同类型的业务场景开发。支持主从热备,提供自动容灾切换、数据备份、故障迁移、实例监控、在线扩容、数据回档等全套的数据库服务。
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档