微服务中台技术解析之 sso 登录实践
背景
当一个公司有了一定规模后,内部各个运营系统或者研发系统都需要进行身份认证,每个系统都需要维护一套账号密码(或者相同的账号密码,重复的校验逻辑),再加上域名的不同,给系统间的登录认证互通带来麻烦。所以有必要抽取身份认证逻辑,统一用户信息管理,形成一个统一的身份认证服务。
虽然我们是在做内部系统时遇到上述问题,但对于登录信息的多账号互通、多域名互通问题,外部系统也存在类似问题,本质上属于 sso 登录范畴。本文以嘉云公司内部系统互通所采用的思路和解决方案为基础,探讨解决 sso 登录的具体实践。
解决思路
sso 登录,通常要解决以下几个问题:
账号认证问题
session 保存问题
多域名 cookie 同步问题
其中账号认证主要保证登录用户是合法用户,可以自建账号体系或者依赖外部账号体系。
session 保存主要保证登录态在一定时间内有效,通常以缓存形式存储用户的基本信息。
多域名 cookie 同步,保证用户只需要在某个系统登录一次,就可以在支持相同账号体系内的其他系统也正常使用,无需再次登录,这是 sso 效果的直接体现。
总体方案
上图是用户使用业务系统时,与 sso 认证中心的交互流程。其中扫码登录/账密登录
解决账号认证问题,生成token/缓存token
解决 session 保存问题,重定向指定地址保存token
解决 cookie 同步问题。
另外,由于我们公司也存在很多非 Java 的业务方,比如 python,Go,Nodejs 等,因此在设计方案时除了经典的 sso-client.jar 包方式外,我们还需要支持 http 接口交互,方便非 Java 业务方接入。最终我们形成的设计方案如下:
下面结合上述流程图和架构图,我们介绍下如何解决 sso 登录的三个问题。
账号认证问题
我们公司内部使用两套账号体系,一个是自建的基于 Confluence 套件(Jira/wiki 等)的账号,还有一套是使用企业微信的账号体系。因此我们的认证既支持企业微信扫码登录,也支持 wiki 账号的账号密码登录。
在具体实现中,我们以企业微信账号体系为主,账号密码登录验证成功后,会拿用户名信息去查询企业微信账号中对应的基本信息(这里有个前提,我们的 wiki 账号体系的用户名和企业微信账号体系的用户名一致,否则需要做映射),如下图所示:
session 保存问题
上图中我们可以看到,在账号认证成功后,会将 session 信息保存到 redis 中。而 session 的唯一标识就是一个 UUID 生成的 token,session 内容包括用户的基本信息,e.g.
而对于 session 的保存期限,我们内部系统默认是保存 7 天,并且会自动刷新 session(根据最新的访问时间向后推迟)。并且,我们还会保存一个当前根域名的 sso cookie,而不同域名下 cookie 同步的问题将在下节介绍。
多域名 cookie 同步问题
在解决多域名 cookie 同步时,我们采用了jump-pass
机制。与此同时,为了支持业务方自定义 cookie 保存逻辑,我们约定了一个配置(needSaveSsoToken=true),会跳转到指定路径下保存 token。总体架构如下:
上图有一个前提,就是用户通过统一登录页成功登录(具体可参考前面几小节),sso 服务中心会将 sso token 写入主域名 a.com。
我们先来看流程比较简单的蓝色线,当业务方配置了 needSaveSsoToken=true
,sso 服务中心会跳转到业务方域名下统一约定路径 ssooAuthToken/save,业务方需要处理这个路径的请求,解析 token,自定义 cookie 保存逻辑。
接下来我们详细介绍下流程略微复杂的绿色线,它详细描述了jump-pass
机制的全过程,基本原理就是 cookie 的同源机制。首先,业务方 sso-client 拦截到未登录的 url 时,会发起 jump.a.com 请求,这个请求会带上主域名下的 sso cookie。sso 服务中心收到这个请求后,获取 sso cookie 值,将其作为 token 参数,回复一个 302 响应,pass.b.com?token=xxx。然后浏览器会重新发起 pass.b.com?token=xxx,这个请求也同样由 sso 服务中心处理,它会将 token 作为 cookie 写入 b.com 域名下,并 302 跳转到业务重定向地址。至此,完成了 cookie 同步过程。整个过程涉及多个 302 重定向过程,大家可以参看流程图理解下。
下面是我们某个系统发生的 jump-pass 过程
存在的问题 &改进方案
请求重放
相信细心的读者已经发现,我们前面提及的jump-pass
机制存在明显的安全问题,如果有人拦截并重放写 cookie 请求(http://pass.b.com?token=xxx),那么将可以获取到该用户的信息。虽然目前在我们内部系统中这不是啥大问题,但对于保存了敏感信息的内部系统来说(e.g. 薪酬系统,绩效系统等),这将是很大的风险。
解决请求重放问题,基本思路就是保证请求的时效性或者增加一个一次性验证因子。
例如,sso 服务中心在返回 pass 302 请求时,加上一个 auth code(http://pass.b.com?token=xxx&code=onetimeCode),该 code 只能使用一次,重放请求将失效,无法写入 sso cookie。
post 请求 302
前面的 cookie 同步方案中,涉及多次浏览器 302 跳转,这对普通 HTTP GET
请求是正常的,但是对于POST
请求来说目前则不支持(详情参考 HTTP RFC2616)。如果要支持 post 的 302 请求,则需要请求的发起端和处理端都额外处理 body 数据,这会增加很多负担,使得 sso 处理逻辑更加复杂。另外,rfc2616 中要求,post 要进行 302 跳转需要用户确认,而且很多 post 请求其实是 ajax 发出的,即使某些 http client 能采用 relax strategy 自动进行 302 跳转,如何带上 cookie 也是比较复杂,对代码侵入很大。
综上所述,没有特殊诉求,一般不建议 sso 同步 cookie 中支持 post 请求 302 跳转。
总结
本文介绍了嘉云公司在 sso 登录中的一些具体实践,提出的方案覆盖了不同业务团队 sso 接入的诉求。并对 sso 中多域名 cookie 同步问题进行了探讨,希望能引起正在进行 sso 登录方案设计的读者一些思考。
如有想法或者疑问,欢迎留言讨论~
评论 (2 条评论)