分布式session
分布式session
(1) session 原理
session存储在服务端,jsessionId存在客户端,每次通过jsessionid取出保存的数据

问题:但是正常情况下session不可跨域,它有自己的作用范围
这个session被sessionManager管理着
| JsessionId列 | 说明 |
|---|---|
| Value | XXXXXX… |
| Domain | gulimall.com要放大域名作用域 |
| Path | / |
| Expires/Max-Age | 40 |
JsessionId列 说明
Value XXXXXX…
Domain gulimall.com要放大域名作用域
Path /
Expires/Max-Age 40
(2) 分布式session解决方案
session要能在不同服务和同服务的集群的共享
1) session复制
用户登录后得到session后,服务把session也复制到别的机器上,显然这种处理很不好

2) hash一致性
根据用户,到指定的机器上登录。但是远程调用还是不好解决

3) redis统一存储
最终的选择方案,把session放到redis中

3) SpringSession整合redis
https://spring.io/projects/spring-session-data-redis
https://docs.spring.io/spring-session/docs/2.4.2/reference/html5/#modules
通过SpringSession修改session的作用域
- 环境搭建
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
修改配置
spring.session.store-type=redis
server.servlet.session.timeout=30m
spring.redis.host=192.168.56.10
添加注解
@EnableRedisHttpSession //创建了一个springSessionRepositoryFilter ,负责将原生HttpSession 替换为Spring Session的实现
public class GulimallAuthServerApplication {
但是现在还有一些问题:
序列化的问题
cookie的domain的问题
- 扩大session作用域
由于默认使用jdk进行序列化,通过导入RedisSerializer修改为json序列化
并且通过修改CookieSerializer扩大session的作用域至**.gulimall.com
@Configuration
public class GulimallSessionConfig {
@Bean // redis的json序列化
public RedisSerializer<Object> springSessionDefaultRedisSerializer() {
return new GenericJackson2JsonRedisSerializer();
}
@Bean // cookie
public CookieSerializer cookieSerializer() {
DefaultCookieSerializer serializer = new DefaultCookieSerializer();
serializer.setCookieName("GULISESSIONID"); // cookie的键
serializer.setDomainName("gulimall.com"); // 扩大session作用域,也就是cookie的有效域
return serializer;
}
}
(4) SpringSession核心原理 - 装饰者模式
网上百度一下:https://blog.csdn.net/m0_46539364/article/details/110533408
就是分析@EnableRedisHttpSession,
@Import({RedisHttpSessionConfiguration.class})
@Configuration( proxyBeanMethods = false)
public @interface EnableRedisHttpSession {
public class RedisHttpSessionConfiguration
extends SpringHttpSessionConfiguration // 继承
implements 。。。{
// 后面SessionRepositoryFilter会构造时候自动注入他
@Bean // 操作session的方法,如getSession() deleteById()
public RedisIndexedSessionRepository sessionRepository() {
SessionRepositoryFilter,每个请求都要经过该filter
public class SpringHttpSessionConfiguration
implements ApplicationContextAware {
@Bean
public <S extends Session> SessionRepositoryFilter<? extends Session> springSessionRepositoryFilter(SessionRepository<S> sessionRepository) { // 注入前面的bean
SessionRepositoryFilter<S> sessionRepositoryFilter = new SessionRepositoryFilter(sessionRepository);
sessionRepositoryFilter.setHttpSessionIdResolver(this.httpSessionIdResolver);
return sessionRepositoryFilter;
}
前面我们@Bean注入了sessionRepositoryFilter,他是一个过滤器,那我们需要知道他过滤做了什么事情:
原生的获取session时是通过HttpServletRequest获取的
这里对request进行包装,并且重写了包装request的getSession()方法
@Override // SessionRepositoryFilter.java
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain) {
request.setAttribute(SESSION_REPOSITORY_ATTR, this.sessionRepository);
//对原生的request、response进行包装
// SessionRepositoryRequestWrapper.getSession()
SessionRepositoryRequestWrapper wrappedRequest = new SessionRepositoryRequestWrapper(
request, response, this.servletContext);
SessionRepositoryResponseWrapper wrappedResponse = new SessionRepositoryResponseWrapper(
wrappedRequest, response);
try {
filterChain.doFilter(wrappedRequest, wrappedResponse);
}
finally {
wrappedRequest.commitSession();
}
}
绣花前面的代码,controller层加参数HttpSession,直接session.setAttribute(“user”,user)即可
前端页面的显示可以用
(5)session的保存
@GetMapping({"/login.html","/","/index","/index.html"}) // auth
public String loginPage(HttpSession session){
// 从会话从获取loginUser
Object attribute = session.getAttribute(AuthServerConstant.LOGIN_USER);// "loginUser";
System.out.println("attribute:"+attribute);
if(attribute == null){
return "login";
}
System.out.println("已登陆过,重定向到首页");
return "redirect:http://gulimall.com";
}
@PostMapping("/login") // auth
public String login(UserLoginVo userLoginVo,
RedirectAttributes redirectAttributes,
HttpSession session){
// 远程登录
R r = memberFeignService.login(userLoginVo);
if(r.getCode() == 0){
// 登录成功
MemberRespVo respVo = r.getData("data", new TypeReference<MemberRespVo>() {});
// 放入session // key为loginUser
session.setAttribute(AuthServerConstant.LOGIN_USER, respVo);//loginUser
log.info("\n欢迎 [" + respVo.getUsername() + "] 登录");
// 登录成功重定向到首页
return "redirect:http://gulimall.com";
}else {
HashMap<String, String> error = new HashMap<>();
// 获取错误信息
error.put("msg", r.getData("msg",new TypeReference<String>(){}));
redirectAttributes.addFlashAttribute("errors", error);
return "redirect:http://auth.gulimall.com/login.html";
}
}

浙公网安备 33010602011771号