SpringSession ¶
session丢失理解 ¶

nginx每次将请求转发到不同的tomcat,对于当前tomcat来说,该次会话无疑是一次新的对话,则会创建一个session,并返回
解决方案 ¶
原理:session不存入 JVM 中(存入redis),或相同的 ip 请求同一个服务器
- 容器共享插件,可以将session存入redis中,不同容器不同的插件,缺点:依赖于容器
- nginx负载均衡时,采用ip_hash,缺点:可能负载均衡失败(单个容器负载过大)
- 自己写一套session会话管理工具
- 采用框架将session存储到其他地方,实现session共享,比如:
SpringSession
基本使用 ¶
导入相关依赖
xml1<!-- springSession 依赖 --> 2<dependency> 3 <groupId>org.springframework.session</groupId> 4 <artifactId>spring-session-data-redis</artifactId> 5</dependency> 6<!-- redis 起步依赖 --> 7<dependency> 8 <groupId>org.springframework.boot</groupId> 9 <artifactId>spring-boot-starter-data-redis</artifactId> 10</dependency>启用springsession,配置类加上
@EnableRedisHttpSession注解java1@EnableRedisHttpSession(maxInactiveIntervalInSeconds= 3600*24, redisFlushMode = RedisFlushMode.ON_SAVE, redisNamespace = "aurora-web") 2 3// maxInactiveIntervalInSeconds: Session默认过期时间,单位秒,默认1800秒,使用 Redis Session 之后,原 Spring Boot 的 server.session.timeout 属性不再生效 4// redisNamespace:配置key的namespace,默认的是spring:session,如果不同的应用共用一个redis,应该为应用配置不同的namespace,这样才能区分这个Session是来自哪个应用的 5// redisFlushMode:配置刷新Redis中Session方式,默认是ON_SAVE模式,只有当Response提交后才会将Session提交到Redis,也可以配置成IMMEDIATE模式,即所有对Session的更改会立即更新到Redis 6// cleanupCron:清理过期Session的定时任务配置redis连接
properties1spring.redis.host=localhost 2spring.redis.port=6379 3#设置session过期时间为 30 分钟 4server.servlet.session.timeout=30m 5#指定 cookie 存放的根路径,用于实现同域名不同项目名 cookie 共享 6server.servlet.session.cookie.path=/ 7#指定 cookie 存放域名,用于实现同域名不同子域名 cookie 共享 8server.servlet.session.cookie.domain=myweb.com查看redis中session
bash1127.0.0.1:6379> KEYS * 21) "spring:session:expirations:1628959080000" 32) "spring:session:sessions:e4839318-ba5a-4eca-92b5-e58bc34be195" 43) "spring:session:sessions:expires:e4839318-ba5a-4eca-92b5-e58bc34be195" 5 6#存在三组数据 7#第一组:hash结构,spring-session存储的主要内容 8127.0.0.1:6379> HKEYS spring:session:sessions:e4839318-ba5a-4eca-92b5-e58bc34be195 91) "sessionAttr:msg" 102) "maxInactiveInterval" #session最大生命周期 113) "creationTime" #创建时间 124) "lastAccessedTime" #最后访问时间 13 14#String结构,用于ttl过期时间记录 15127.0.0.1:6379> get "spring:session:sessions:expires:e4839318-ba5a-4eca-92b5-e58bc34be195" 16"" 17 18#set结构,过期时间记录 19127.0.0.1:6379> SMEMBERS spring:session:expirations:1628959080000 201) "\xac\xed\x00\x05t\x00,expires:e4839318-ba5a-4eca-92b5-e58bc34be195"
使用场景(cookie存放规则修改) ¶
默认
同域名,同项目名,可以实现session共享,主要依赖于浏览器何时携带 cookie 中的 sessionId
同域名,不同项目名
同域名,不同项目名,会因为cookie的path属性,而不能携带

上图 /one 路径下所有请求都会携带cookie,不同项目名就不会携带,cookie丢失
通过
配置cookie序列化规则bean,来改变cookie存放规则,springboot也可以通过配置文件配置cookie存放位置
1@Bean
2DefaultCookieSerializer defaultCookieSerializer(){
3 DefaultCookieSerializer cookieSerializer=new DefaultCookieSerializer();
4 cookieSerializer.setCookiePath("/"); //指定cookie存放的路径,不同项目名cookie共享
5 cookieSerializer.setDomainName("myweb.com"); //指定cookie的domain,不同子域名可以进行cookie共享
6 return cookieSerializer;
7}此时如下图: / 请求下都会携带cookie

知识点 ¶
Cookie
- 理论上Cookie是不可以跨域名的,隐私安全机制禁止网站非法获取其他网站的Cookie
- 理论上同一个一级域名下的两个二级域名也不能交互使用Cookie
- path属性决定允许访问Cookie的路径。比如,设置为"/“表示允许所有路径都可以使用Cookie
原理简要分析
当请求进来的时候,SessionRepositoryFilter 会先拦截到请求,将 request 和 response 对象转换成 SessionRepositoryRequestWrapper 和 SessionRepositoryResponseWrapper 。后续当第一次调用 request 的getSession方法时,会调用到 SessionRepositoryRequestWrapper 的getSession方法。这个方法是被重写过的,逻辑是先从 request 的属性中查找,如果找不到;再查找一个key值是"SESSION"的 Cookie,通过这个 Cookie 拿到 SessionId 去 Redis 中查找,如果查不到,就直接创建一个RedisSession 对象,同步到 Redis 中。