spring session 实现单用户多账号登录
前言
昨天在查看 spring session 官网时发现一段有趣的话(http://projects.spring.io/spring-session/):
Multiple Browser Sessions - Spring Session supports managing multiple users’ sessions in a single browser instance (i.e. multiple authenticated accounts similar to Google).
这段话大意就是说 spring session 支持一个用户在浏览器上登录多个账户的功能。一般情况下,我们是通过开启多个浏览器同时登录某个系统,实现自己多个账户的同时操作。其实,Chrome 浏览器已经实现了多个 Google 账户在浏览器上的操作。
个人参考案例
个人博客 : https://zggdczfr.cn/
个人参考案例(如果认可的话请给个star) : https://github.com/FunriLy/springboot-study/tree/master/%E6%A1%88%E4%BE%8B10
正式实现
这里我用的是 spring boot 学习(十四)SpringBoot+Redis+SpringSession缓存之实战,这个前天的案例。
通过仔细阅读官方的操作文档 Spring Session - Multiple Sessions,我稍微理解了该如何来使用多账户登录。一般情况下,我们登录访问系统时,Spring Session 会默认一个请求参数_s
为 0.通过修改这个参数,来启用 SpringSession 为用户分配新的 SessionId。
实现机制
通过稍微阅读 SpringSession 源码 与几位大神的博客,我大概知道了其中的机制。
* spring session通过增加session alias概念来实现多用户session,每一个用户都映射成一个session alias。当有多个session时,spring会生成“alias1 sessionid1 alias2 sessid2…….”这样的cookie值结构。
* 每当spring session提交时如果有新session生成,会触发onNewSession动作生成新的session cookie。
onNewSession 是 Spring Session 源码之中一个重要的方法
注意:如果只有一个session id,则和普通session cookie一样处理,cookie值就是session id。如果存在多个session id,就会形成上面提到的那种 alias 结构的 session cookie。
坑
问题
等我信心满满等按照官网操作方法去搞时,却失败了,爆了个异常
java.lang.IllegalArgumentException: An invalid character [32] was present in the Cookie value
报得我一脸蒙蔽……google之后,原来是 Tomcat 在 8.5.X 版本后不使用 Servlet 3.1
的标准,改用RFC 6265
来实现。这样子的话,会导致报文输入时多了个空格(字符32)。
解决方案
重写 Tomcat 的解析方法,代码来源于Github,并经过修改。
@Configuration
public class CookieConfig {
@Bean
public EmbeddedServletContainerCustomizer customizer(){
return new EmbeddedServletContainerCustomizer() {
@Override
public void customize(ConfigurableEmbeddedServletContainer container) {
TomcatEmbeddedServletContainerFactory tomcat = (TomcatEmbeddedServletContainerFactory) container;
tomcat.addContextCustomizers(
new TomcatContextCustomizer() {
@Override
public void customize(Context context) {
context.setCookieProcessor(new LegacyCookieProcessor());
}
});
}
};
}
}
将方法重写后注入 bean 中,交给容器去管理。
测试
启动工程,在同一个浏览器中分别访问
http://localhost:8080/session?_s=0
http://localhost:8080/session?_s=1
http://localhost:8080/session?_s=2
我们可以看到结果(三个sessionId都不一样):
{"SessionId":"a1d59666-10a4-4dc3-a1b0-1166cda8aab2","ServerPort":"服务端口号为 8080"}
{"SessionId":"21177055-70e2-426c-9292-40ccf825078b","ServerPort":"服务端口号为 8080"}
{"SessionId":"1eade000-f2e1-4bf9-9aef-1510d39441e4","ServerPort":"服务端口号为 8080"}
参考资料
- https://github.com/spring-projects/spring-session
- http://docs.spring.io/spring-session/docs/current/reference/html5/guides/users.html
- https://github.com/spring-projects/spring-boot/issues/6827
- (配置代码主要来源)https://github.com/jomolina/session-couchbase-spring-boot-starter/commit/e7b0487b92b65408c485a7d25b89a64c9fb4fc89