1. 单点登录 Single Sign On(SSO)指在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的系统(多用于同一家公司的多个不同产品)。
2. 单点登录的必要条件——认证中心,用于将用户登录,注册,修改用户信息等对用户的基本操作全部抽离出来单独维护。
3. 重点难题就在于如何将认证中心对用户的认证操作适配到所有的子系统中?
标准做法:CAS,OAuth2.0
非标做法:根据业务需求定制化单点登录模型。
但是无论怎么做,核心就三种解决方案
4. 解决方案1:Session + Cookie 模式
- 用户在认证中心登录成功后会在服务器上存一个session,是一个键值对的数据形式(键就是全局唯一ID,注意不是用户ID,值就是用户的身份信息,id,名字等等) 换句话说,只要这个数据形式里面存有这个用户的键值对那么这个用户就算登录成功了,反之就没登陆成功或者是登录已过期。 这个数据形式一般存在数据库或者Redis缓存中。
- 登录成功后会把之前生成的全局唯一id——sessionid下放到用户这里,存在浏览器的cookie中。此后,当用户去访问子系统的时候请求会将这个sessionid带上,以判断登录情况。(前提条件,子系统之间得是相同的站点。如果是不同的站点去做的话需要特殊处理,不同站点的话正常情况是拿不到这个cookie的)
- 被访问的子系统在拿到这个sessionid的时候,需要再次拿这个id去认证中心确认用户的登录状态,若登录成功,此子系统才可以正常访问。
优势:认证中心拥有对用户的绝对控制力,可以随时让用户下线。若我直接去删掉用户的sessionid,那么用户在下一次请求资源的时候,这个系统去认证中心验证时就无法确认用户的登录状态,继而用户就会被引导到认证中心去重新登录。
劣势:
- 成本很高,如果用户数量非常大,子系统非常多,子系统对认证中心的请求会变得非常频繁,认证中心服务器的压力会非常大(高并发,高负载),同时存储用户登录信息的表也会非常大(查询慢导致慢接口,用户体验性下降)。
- 风险也很高,如果认证中心挂掉了,那么所有的子系统都挂掉了(需要做容灾)。
- 还有一种情况,就是某个子系统运行的很好,用户量很大,那么这个子系统需要扩容。但是子系统扩容那么认证中心也需要扩容,因为子系统的增量会带动认证中心的增量,这就显得很鸡肋。
5. 解决方案2: 单Token模式
- 用户在认证中心登录成功后,不需要做任何操作,直接生成一个不可被篡改的token(可以用JWT)发送给用户,用户自己存储。
- 访问子系统的时候会将这个token带过去,这里的区别来了,子系统不需要再去认证中心确认用户的登录状态,子系统可以自行认证这个token。(子系统和认证中心交换一个密钥,子系统可以用这个密钥来解密token,来确认用户的登录状态)
- 验证通过后就可以给用户正常访问了。
优势:
- 不烧钱,某个子系统如果需要扩容,认证中心是不需要跟着去扩的
- 认证中心的压力非常小。
缺点:对用户的控制能力会非常弱。返回给用户的token要设置过期时间(那这个过期时间设置多久合适呢?太短了用户需要频繁的登录,太长了用户的违规操作不能让他及时下线),想要下线得让用户中心去通知每一个子系统哪个用户需要下线。
6. 解决方案3: 双Token模式
- 登录后给用户两个token,一个是过期时间较短的短token(用户主要用这个token子系统确认登录状态) ,另一个是过期时间较长的token(用于维持最久的登录状态)。
- 用户用短token去访问子系统(这个短token一般过期时间就是十几二十分钟),若短token过期,长token没过期(长token一般过期时间很长,一两个月)。那么当前子系统的此次请求返回失败,用户就会用长token去认证中心换一个新的短token。重新拿这个新的短token去访问子系统。
缺点:几乎没有太大的缺点。
优势:弥补了 session+cookie烧钱的缺陷,解决了单token对用户控制力度差的问题。
核心思想说白了就是让用户每隔一小段时间来用户中心一次,加强用户中心对用户的控制力度,将用户的控制权转回到认证中心。