最近在尝试在集群中使用 keycloak 的时候遇到一个问题,keycloak 认证后 session 的共享,简单的说就是我想在不同的实例(包括相同镜像的不同实例,以及不同镜像的实例)中共享认证信息。
不同镜像实例不要共享 session
这只是一个建议,以及目前比较优秀的实践。
不要共享 session 的原因在于两个方面:编码难度和安全。
设计编码
想象一下在 spring 和 node.js 这种后端上共享 session 至少需要做哪些事情:
- 设计 session 的存取
需要考虑的有 session 在中间件种的过期处理,以及通过 cookie 查找 session 的效率等。比如 spring-session-redis 的结构大体如下:
(使用了 string, hash, set 等)
这种实现是没有规范作为依循的,也就意味着如果你想在 node 上使用同样的设计,那么你就需要自己去实现了。

- 设计 session 对象的序列化
这个序列化是在不同镜像中使用的,那么意味着它需要一个多语言间的标准的模型(目前国内又有多少项目有一个统一的标准模型呢?😀)。现在一般使用 json 作为不同语言之间数据的中间表示,这会方便很多,但是一想到要写两套一模一样的模型就有点烦,不知道有没有面向模型的生成器可以用(类似 open-api 生成 sdk)。
零信任
处于安全考虑,我希望即使在共享 session 的情况下,依然进行必要的验证(java 实例不信任 node 实例赋予的 cookie),那么就算有相应的共享会话,那么还是需要进行用户的身份验证,session 中共享的认证相关信息就显得多余了。
单点登录(sso)
如果我既希望安全(应用实例间没有相互信任)又不希望用户多次登录呢?
那么可以考虑单点登录,由一个单独的服务去记录用户的共享登录状态,每次需要认证的时候先和这个服务交互,由这个服务作为用户的代理完成身份的相关认证,(从易于理解的方面,我们可以想象成用户自己开了一个服务器作为自己的代表,这个服务器需要用户进行认证后才能使用,之后一段实践可以代用户进行认证)。
也就不需要共享会话去维持一个不同镜像实例间的共同登录态了。
(如果是同一个镜像的实例用于负载均衡的话,还是建议使用共享的会话,减少认证产生的消耗)。