单点登录即Signle Sign On,简称SSO。其解决的是用户在多个站点之间跳转时需要频繁登录的问题,比如用户登录了天猫,就应该无需再使用账号登录淘宝,它们之间是可以相互信任的,应该自动同步登录状态。从这点上看单点登录技术的本质是登录状态在多个站点之间的扩散机制。
先来看下单个程序的登录处理流程,这里以Web应用程序为例。在单个Web应用程序中用户通过浏览器提交账号信息到后端服务,后端服务验证账号的有效性,验证通过后在后端创建一个Session,用来维持用户的登录状态,然后向前端返回一个SessionId,前端将这个SessionId保存起来,这个保存操作一般是写入当前域名对应的Cookie,之后每次请求Web页面的时候都要携带这个SessionId,后端服务根据这个SessionId就能识别是哪个用户,然后就可以进行对应的业务处理。具体登录流程如下图所示:
那如何在多个站点之间共享登录状态呢?如果是同一个顶级域名下的不同子域名站点,可以通过共享Cookie的方式,具体操作就是设置Cookie的Domain属性为同一个顶级域名,则各个站点都能获取到这个Cookie,也就能获取到同一个Session,实现登录状态的共享。不过这种方式的普适性不太好,不同的域名之间就做不到,而且共享Cookie安全性上也会有点问题。
本文将介绍一种更通用的单点登录方式,通过引入一个SSO站点,所有的登录请求和登录状态同步请求都交给这个站点来处理。这里假设有两个站点需要同步登录状态:站点A 和 站点B,用户访问站点A时没有登录过,需要在SSO站点先登录,然后登录状态同步到站点A,访问站B时,因为用户已经登录过,所以只需要将登录状态同步到站点B即可。一图胜千言,下面给出详细的时序图,并做一些关键步骤说明,具体过程稍微有些复杂,不过仔细阅读,还是比较容易理解的。
步骤5: 用户在业务站点未登陆时,统一重定向到SSO站点的登录页面。这里有一点需要注意:重定向时需要携带用户访问A站点的地址,这样成功登录后才能跳转回来。
步骤6和步骤27: 重定向到SSO站点时,有两点需要注意:一是要检查来源,如果不是允许的业务站点,则应该拒绝请求,这样比较安全;二是会检查用户在SSO站点的登录状态,如果登录过,浏览器会在HTTP请求头中携带SessionId的Cookie,根据这个SessionId可以判断用户登录状态是否有效,如果有效则给业务站点返回一个Ticket,如果无效则返回登录页面内容,需要用户先通过账号登录成功,然后再给业务站点返回Ticket。
步骤15和步骤30: 业务站点收到SSO站点的Ticket之后,还要使用事先分配好的密钥计算一个签名,这个密钥每个站点应该不同,业务站点后台携带这个Ticket和签名去SSO站点进行验证。Ticket是SSO站点发放的,自然能够验证其有效性;密钥也是SSO站点发放的,根据签名的计算规则再算一遍,对比下就知真伪了。然后告知业务站点你可以登录了,并发送必要的用户信息。之所以会有这个步骤,是因为来源的验证不是那么可靠,密钥每家独一份,安全性就高了很多。
步骤18和步骤33: 业务站点都需要创建自己的用户Session,然后在浏览器写入对应业务站点的SessionId Cookie。这个Session在服务端可以分别存储,如果业务规模不大也可以使用同一份存储,统一存储时登录状态可以统一管理。
当前有很多的站点使用JWT做登录认证,这样有个好处就是分布式环境下不需要集中认证,每个部署节点都拥有完全的认证能力,不过如果需要注销用户的时候,就很难实现同步注销。有一些解决方案,比如认证Token的生存期短一点,再增加一个刷新Token,刷新Token时访问集中的认证服务;或者使用一个高效的黑名单机制,每次都检查黑名单等。如果业务规模不是很大,都不如统一Session的机制简单,比如使用Redis存储Session信息,轻松处理千级万级并发。
以上就是本文的全部内容了,重点就是那张单点登录的时序图,想不明白的时候可以多撸几遍,如有错漏欢迎指正。