这里,我们选用spring session 的redis存储模式来解决分布式session的问题
一、整合与使用
1.引入依赖(注:前提是你的项目中要有 redis 依赖哦!)
<!-- 整合springsession -->
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
2.启动类中开启 springsession
@EnableRedisHttpSession //开启springsession
3.在配置文件中指定springsession存储介质为redis
spring.session.store-type=redis
4.想要在多个子域名中都可以使用json的形式存储session,我们还需要对作用域和序列化方式进行配置
package com.xxx.xxx.product.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.session.web.http.CookieSerializer;
import org.springframework.session.web.http.DefaultCookieSerializer;
/**
* @Description: springSession配置类
* @Created: with IntelliJ IDEA.
* @author: LY
* @createTime: 2020-06-29 13:36
**/
@Configuration
public class MySessionConfig {
@Bean
public CookieSerializer cookieSerializer() {
DefaultCookieSerializer cookieSerializer = new DefaultCookieSerializer();
//放大作用域,让session可以在各个子域名中读到
cookieSerializer.setDomainName("xxx.com");
//设置session-cookie名称
cookieSerializer.setCookieName("XXSESSION");
return cookieSerializer;
}
//让redis中的数据可以 用json形式存储
@Bean
public RedisSerializer<Object> springSessionDefaultRedisSerializer() {
return new GenericJackson2JsonRedisSerializer();
}
}
这里进行一下说明,我们的session在客户端记录成cookie时是有作用域的,比如你在 ss.xxx.com 生成的cookie ,里面存储的作用域就是 ss.xxx.com ,所以我们需要扩大他的作用域 为 ”.xxx.com“ 后,才能使各个子域名下的微服务进行session互通
如百度的:
5 . 正常使用session即可(示例)
/**
1.设置session 的 值****************************************
**/
//你的实体类
String userInfo= new UserInfo();
//转为接送字符串
String userInfoString = JSONObject.toJSONString(userInfo);
//1.设置session 的 值
session.setAttribute("userInfo", userInfoString);
/**
2.获取session 的 值****************************************
**/
//2.获取session
String userInfoSessionStr = session.getAttribute("userInfo");
二、核心原理
-
我们来看看 @EnableRedisHttpSession 注解中干了些什么
1.1 可以看到 导入了 RedisHttpSessionConfiguration.class 这个配置类a.进入到RedisHttpSessionConfiguration配置类中我们可以看到,首先他加载了RedisOperationsSessionRepository 这个组件,主要用于redis 持久层的增删改查操作。
b. 他还继承了一个配置类 SpringHttpSessionConfiguration
我们来看看这个继承的类里面干什么核心事情,核心就是他,里面有个SessionRepositoryFilter 过滤器
那这个 SessionRepositoryFilter 又是个什么呢? 进入到SessionRepositoryFilter 我们可以看到他继承了 OncePerRequestFilter,再往下追 OncePerRequestFilter ,OncePerRequestFilter 又 实现了 Filter, 这个 Filter 其实就是我们经常用到的过滤器,那我们就明白了,其实每一次的请求进来时都会被这个过滤器所捕捉到(SessionRepositoryFilter)
c. 我们回到RedisHttpSessionConfiguration配置类中来看看 RedisOperationsSessionRepository 这个组件都有些什么方法(ALT+7 或者 CTRL+F12), 可以看到里面有很多session方法,与我们常用的session 操作方法是不是很像?对的,springsession就是通过这些方法来重写原session的curd方法后,来操作分布式session的。
d. 我们在来看 SessionRepositoryFilter ,里面有个重写方法 doFilterInternal ,其实核心就是他。
可以看到里面有两个包装类:
//包装原始请求对象
SessionRepositoryRequestWrapper wrappedRequest = new SessionRepositoryRequestWrapper(
request, response, this.servletContext);
//包装原始响应对象
SessionRepositoryResponseWrapper wrappedResponse = new SessionRepositoryResponseWrapper(
wrappedRequest, response);
try {
//将包装后的对象应用到我们后面的整个执行链中
filterChain.doFilter(wrappedRequest, wrappedResponse);
}
finally {
wrappedRequest.commitSession();
}
e. 我们在使用session时一般是这样的。其中的session是由request.getSession()获得的,然而我们前面的一步中经过了过滤器其实HttpServletRequest 的session已经被包装过了,并且这里我们获得session方法getSession() 方法也是被重写过的,所以就完成了使用 redis 存储获取session的实现。
设计模式为:装饰着模式。
其他: springsession使用redis分布式session 其实也解决了我们session应用的所有问题,存储后默认过期时间为30分钟,只要我们浏览器不关闭,我们的session就会在redis中不断续期,直到我们的浏览器关闭。