这里采用服务端同一存储的方式解决分布式Session共享问题
环境准备
1.创建三个服务器
ac为认证服务器,oth1、oth2分辨扮演其他业务服
2.设置本地域名
C:\Windows\System32\drivers\etc
这里设置域名也是模拟线上环境,因为不同域名存在session跨域问题,
3.导入依赖-这里的依赖
4.编写伪代码
ac服务器
login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登录</title>
</head>
<body>
<form action="/login" method="post">
<ul>
<li class="top_1">
<input type="text" name="username" placeholder=" 邮箱/用户名/已验证手机" class="user" />
</li>
<li>
<input type="password" name="password" placeholder=" 密码" class="password" />
</li>
<li class="ent"><button type="submit" class="btn2"><a>登 录</a></button></li>
</ul>
</form>
</body>
</html>
ok.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>ac-ok</title>
</head>
<body>
登录成功!!!
欢迎[[${session.data}]]
</body>
</html>
controller
@PostMapping("/login")
public String list(UserEntity userEntity, HttpSession session){
log.debug("ac");
UserEntity userRes=userService.query(userEntity);
session.setAttribute("data","ac");
log.debug(userRes.toString());
return "ok";
}
//这里还缺一个login.html的视图解析器
//自己创建一个/映射一下
oth1服务器
oth1.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>oth1-ok</title>
</head>
<body>
登录成功!!!
欢迎[[${session.data}]]
</body>
</html>
controller
@GetMapping("/Oth1")
public String list( HttpSession session){
log.debug("Oth1");
return "oth1";
}
oth2服务器
oth2.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>oth2-ok</title>
</head>
<body>
登录成功!!!
欢迎[[${session.data}]]
</body>
</html>
controller
@GetMapping("/Oth2")
public String list( HttpSession session){
log.debug("Oth2");
return "oth2";
}
5.访问测试
这里采用火狐浏览器,其实我个人比较喜欢谷歌浏览器,但是公司有项目开发,还有一些个人账号信息存储在谷歌浏览器里面,不想被清空数据从新登陆,所以就用火狐这个备胎了!!!
AC登陆
登陆成功
登陆ac服务器成功,取的ac服务器的session
访问Oth1界面
这里虽然存在session但是在代码里面没有给Oth1服务器的session中存值
访问Oth2界面
这个Oth2服务器索性连session都没有
问题分析
不同服务器间session不共享,不同域名下session数据不共享,controller默认不携带session数据!
这里不同服务器session数据不共享还好理解,但是不同域名下session数据不共享就有点懵逼,演示一下,把session的作用域扩大到test.com范围在尝试,服务器不做变动!
在浏览器上将ac的session的域范围扩大为.test.com然后在Oth1、Oth2界面刷新,查看到Oth1、Oth2的cookie中确实多出的test.com的session,那么Oth1、Oth2的cookie中都存在session那么就能理所应当像ac一样取出session中的值,渲染为111,这里其实并不是,虽然session的域范围Oth1、Oth2确实是有了,但是在html界面渲染时Oth1、Oth2服务器的session里面并没有存在这样的值,所以Oth1、Oth2的html界面在服务器渲染后的session的值也是没有的!
session数据共享问题,spring为我们提供了非常成熟的技术方案!SpringSession,使用redis做session的统一存储,让各服务器都能共享redis中的session数据
1.导入MVN依赖
<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>
session:
store-type: redis
servlet:
session:
timeout: 30m
4.测试访问
查看登录后的AC服务器session
查看redis
这里现在分配的作用域是子域名的,现在还不能跨域共享数据
5.手动修改ac的session的作用域
6.刷新Oth2界面,服务器不做任何变动
这里Oth2服务器的代码没动,浏览器刷新Oth2的时候会检测当前域名,上个步骤中将AC的二级ac.test.com的作用域设置为了test.com所以Oth2.test.com也会获得父域名下的session数据,这里我们是手动修改的session的作用域,那么我们在后端用代码实现!
1.默认令牌为session=xxxxxxxxxxxxxx
2.作用域为当前服务器访问域名
补充:这里由于是演示代码,session中存储的是简单字符串,如果在多系统环境中,不同语言的数据类型不一样,会导致数据无法解析,那么可以是采用JSON来解决数据兼容性问题
添加SpringSession配置类
@Configuration
public class SessionConfig {
@Bean
public CookieSerializer cookieSerializer() {
DefaultCookieSerializer cookieSerializer = new DefaultCookieSerializer();
cookieSerializer.setDomainName("test.com");
cookieSerializer.setCookieName("TESTSESSION");
return cookieSerializer;
}
@Bean
public RedisSerializer<Object> springSessionDefaultRedisSerializer() {
return new GenericJackson2JsonRedisSerializer();
}
}
重启服务器
访问ac
这里的session的作用域为test.com
查看Oth2
这里访问直接能够拿到父域test.com的值
Redis中session的JSON序列化
分布式Session的解决方案就演示完成了,后面还会结合单点登录,整合SpringSecurity来做一个整体的案例