spring session 单用户多账号登录

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 单用户多账号登录

前言

昨天在查看 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。

问题

等我信心满满等按照官网操作方法去搞时,却失败了,爆了个异常

1
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,并经过修改。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@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都不一样):

1
2
3
4
5
{"SessionId":"a1d59666-10a4-4dc3-a1b0-1166cda8aab2","ServerPort":"服务端口号为 8080"}
{"SessionId":"21177055-70e2-426c-9292-40ccf825078b","ServerPort":"服务端口号为 8080"}
{"SessionId":"1eade000-f2e1-4bf9-9aef-1510d39441e4","ServerPort":"服务端口号为 8080"}

参考资料

评论