SpringSesion共享使用、自定义Session作用域
通常情况下,Tomcat、Jetty等Servlet容器,会默认将Session保存在内存中。如果是单个服务器实例的应用,将Session保存在服务器内存中是一个非常好的方案。但是这种方案有一个缺点,就是不利于扩展。
目前越来越多的应用采用分布式部署,用于实现高可用性和负载均衡等。那么问题来了,如果将同一个应用部署在多个服务器上通过负载均衡对外提供访问,如何实现Session共享?
实际上实现Session共享的方案很多,其中一种常用的就是使用Tomcat、Jetty等服务器提供的Session共享功能,将Session的内容统一存储在一个数据库(如MySQL)或缓存(如Redis)中。
下面我们将在springcloud微服务项目中,使用第三方存储服务保存(redis),但是如果是直接使用redis,要求对我们的代码进行修改。所以使用spring session。spring session的原理是对我们的request和response进行了包装。因为session的获取是request.getSession() ,所以包装了请求体,而cookie的设置需要设置到response中所以也包装了响应体。spring session很完美的解决了代码重构的问题。还能对接不同的存储中间件,不仅仅限于redis。子域共享问题。自定义cookie的响应信息。设置cookie 的作用域domean 只能是当前域名或者父域名。domean 是子域名可以拿到父域名的信息,domean是父域名不能拿到子域名的信息。
代码
Sample Applications that use Spring Boot session redis guide
Sample Applications that use Spring Java-based configuration session redis guide
[HttpSession with Redis JSON serialization](https://github.com/spring-projects/spring-session/tree/2.1.12.RELEASE/samples/boot/redis-json)
依赖
<!-- 整合spring session完成session 共享问题 -->
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
<!-- redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
配置文件
# redis的连接信息
spring.redis.host=192.168.1.10
spring.redis.port=6379
# 将session数据保存到redis中
spring.session.store-type=redis
spring.session.redis.flush-mode=on_save
spring.session.redis.namespace=spring:session
启用自动配置(启动类配置 )
@EnableRedisHttpSession // 整合redis作为session存储,就是通过filter包装了我们的请求体和响应体
@EnableFeignClients
@EnableDiscoveryClient
@SpringBootApplication
public class GulimallAuthServerApplication {
public static void main(String[] args) {
SpringApplication.run(GulimallAuthServerApplication.class, args);
}
}
Spring sessioin 中redis的序列化、cookie的自定义设置
/**
*
* Description:设置Session作用域、自定义cookie序列化机制
* date:2020/11/5
*/
@Configuration
public class GlMallSessionConfig {
@Bean
public CookieSerializer cookieSerializer(){
DefaultCookieSerializer cookieSerializer = new DefaultCookieSerializer();
// 明确的指定Cookie的作用域
cookieSerializer.setDomainName("gulimall.com");
cookieSerializer.setCookieName("FIRESESSION");
return cookieSerializer;
}
/**
* 自定义序列化机制
* 这里方法名必须是:springSessionDefaultRedisSerializer
*/
@Bean
public RedisSerializer<Object> springSessionDefaultRedisSerializer(){
return new GenericJackson2JsonRedisSerializer();
}
}
---存入session(统一存储到redis,进行服务之间进行共享Session)
@PostMapping("/login")
public String login(UserLoginVo userLoginVo, RedirectAttributes redirectAttributes, HttpSession session){
//远程登录
R login = memberFeignService.login(userLoginVo);
if (login.getCode()==0){
//登录成功
MeberRespVo meberRespVo = login.getData("data", new TypeReference<MeberRespVo>() {
});
//存入session(统一存储到redis,进行服务之间进行共享Session)
session.setAttribute("loginUser",meberRespVo);
return "redirect:http://gulimall.com";
}else {
HashMap<String, String> errors = new HashMap<>();
errors.put("errors",login.getData("msg",new TypeReference<String>(){}));
redirectAttributes.addFlashAttribute("errors",errors);
return "redirect:http://auth.gulimall.com/login.html";
}
}
在获取session的时候不同服务只需要在获取 redis中session的key即可(session.loginUser.nickname)loginUser为key。
注意:需要使用共享Session的服务都要进行 以上的配置,不然使用不了存在 redis中的Session。
Spring Session原理
Session核心原理:
1)、@EnableRedisHttpSession导入RedisHttpSessionConfiguration配置
1、给容器添加了一个组件RedisIndexedSessionRepository
RedisIndexedSessionRepository:redis操作session。session的增删改查封装类
2、SessionRepositoryFilter --》Filter: session存储的过滤器,每个请求都必须经过filter
1、创建的时候 ,就自动从容器中获取SessionRepository
2、原始的request,response都被包装。SessionRequestWrapper,SeesionRepositoryResponseWrapper
3、以后获取session。request.getSession();
//SessionRepositoryRequestWrapper
4、wrappedRequest.getSession()==>SessionRepositry中获取的。
装饰者模式:把原生的请求封装成自己的
Seesion会自动延期,redis中也是有过期时间的
#### 暑假到现在很久没更新了 因为在学校没时间(好吧其实就是因为自己懒哈哈,在学校安逸过头了)、今天刚弄完谷粒商城分布式Session共享的问题,想要更加详细的知识关注:https://gitee.com/jinronga/guilimall,不要停止我们的脚步。继续努力!