SpringSession的源码解析(从Cookie中读取Sessionid,根据sessionid查询信息全流程分析)

本文涉及的产品
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
云数据库 Tair(兼容Redis),内存型 2GB
云解析 DNS,旗舰版 1个月
简介: 上一篇我们介绍了SpringSession中Session的保存过程,今天我们接着来看看Session的读取过程。相对保存过程,读取过程相对比较简单。本文想从源码的角度,详细介绍一下Session的读取过程。

前言

上一篇我们介绍了SpringSession中Session的保存过程,今天我们接着来看看Session的读取过程。相对保存过程,读取过程相对比较简单。

本文想从源码的角度,详细介绍一下Session的读取过程。

读取过程的时序图

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

getSession(true)方法。

@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()方法

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类。

private HttpSessionIdResolver httpSessionIdResolver = new CookieHttpSessionIdResolver();

所以,SessionRepositoryFilter.this.httpSessionIdResolver的实例是一个CookieHttpSessionIdResolver对象。

而SessionRepositoryFilter.this.sessionRepository的实例是一个RedisOperationsSessionRepository对象。

那么接下来我们就分别来看看这个两个类的相关方法。

resolveSessionIds方法

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

@Override
  public List<String> resolveSessionIds(HttpServletRequest request) {
  return this.cookieSerializer.readCookieValues(request);
  }

看到这个方法之后,我们发现这个方法只是一个中转方法,内部直接把请求交给了readCookieValues方法。同样的在CookieHttpSessionIdResolver类内部也定义了cookieSerializer这个属性,

它的实例对象是DefaultCookieSerializer。所以,真正的操作逻辑还是在DefaultCookieSerializer类中完成的。

private CookieSerializer cookieSerializer = new DefaultCookieSerializer();

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

readCookieValues方法

@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信息。

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保存的所有数据

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中的数据的全过程,希望对大家有所帮助。

相关文章
|
1月前
|
机器学习/深度学习 数据采集 存储
时间序列预测新突破:深入解析循环神经网络(RNN)在金融数据分析中的应用
【10月更文挑战第7天】时间序列预测是数据科学领域的一个重要课题,特别是在金融行业中。准确的时间序列预测能够帮助投资者做出更明智的决策,比如股票价格预测、汇率变动预测等。近年来,随着深度学习技术的发展,尤其是循环神经网络(Recurrent Neural Networks, RNNs)及其变体如长短期记忆网络(LSTM)和门控循环单元(GRU),在处理时间序列数据方面展现出了巨大的潜力。本文将探讨RNN的基本概念,并通过具体的代码示例展示如何使用这些模型来进行金融数据分析。
197 2
|
2月前
|
存储 Cloud Native 关系型数据库
Ganos实时热力聚合查询能力解析与最佳实践
Ganos是由阿里云数据库产品事业部与飞天实验室共同研发的新一代云原生位置智能引擎,集成于PolarDB-PG、Lindorm、AnalyticDB-PG和RDS-PG等核心产品中。Ganos拥有十大核心引擎,涵盖几何、栅格、轨迹等多种数据处理能力,实现了多模多态数据的一体化存储、查询与分析。本文重点介绍了Ganos的热力瓦片(HMT)技术,通过实时热力聚合查询与动态输出热力瓦片,无需预处理即可实现大规模数据秒级聚合与渲染,适用于交通、城市管理、共享出行等多个领域。HMT相比传统网格聚合技术具有高效、易用的优势,并已在多个真实场景中验证其卓越性能。
51 0
|
1月前
|
缓存 Java Spring
servlet和SpringBoot两种方式分别获取Cookie和Session方式比较(带源码) —— 图文并茂 两种方式获取Header
文章比较了在Servlet和Spring Boot中获取Cookie、Session和Header的方法,并提供了相应的代码实例,展示了两种方式在实际应用中的异同。
136 3
servlet和SpringBoot两种方式分别获取Cookie和Session方式比较(带源码) —— 图文并茂 两种方式获取Header
|
29天前
|
存储 SQL 分布式计算
湖仓一体架构深度解析:构建企业级数据管理与分析的新基石
【10月更文挑战第7天】湖仓一体架构深度解析:构建企业级数据管理与分析的新基石
46 1
|
1月前
|
域名解析 网络协议 安全
DNS查询工具简介
DNS查询工具简介
|
2月前
|
域名解析 网络协议 安全
DNS查询工具简介
DNS查询工具简介
|
25天前
|
SQL 数据可视化 BI
SQL语句及查询结果解析:技巧与方法
在数据库管理和数据分析中,SQL语句扮演着至关重要的角色
|
2月前
|
存储 缓存 自然语言处理
深度解析ElasticSearch:构建高效搜索与分析的基石
【9月更文挑战第8天】在数据爆炸的时代,如何快速、准确地从海量数据中检索出有价值的信息成为了企业面临的重要挑战。ElasticSearch,作为一款基于Lucene的开源分布式搜索和分析引擎,凭借其强大的实时搜索、分析和扩展能力,成为了众多企业的首选。本文将深入解析ElasticSearch的核心原理、架构设计及优化实践,帮助读者全面理解这一强大的工具。
168 7
|
2月前
|
存储 安全 NoSQL
Cookie、Session、Token 解析
Cookie、Session、Token 解析
58 0
|
2月前
|
监控 安全 网络安全
恶意软件分析:解析与实践指南
【8月更文挑战第31天】
163 0

推荐镜像

更多