单点登录(Single Sign On),简称为 SSO,是解决企业内部的一系列产品登录问题的方案。SSO 的定义是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统,用于减少用户重复的登录操作,提升用户体验。从技术层面上讲,单点登录目前有多种实现方案,本文从博主个人的理解出发,比较这几种不同方案的优劣和适用场景。

一、单点登录的前提

单点登录的前提是,拥有统一的登录账号,统一的授权中心,登录一次就可以访问所有相互信任的应用系统。

简单来说,就是用户的一个登录凭证可同时供多个系统使用,或者登录凭证通过授权中心进行某种转换后可以提供给各自的系统使用,这种转换可在前端进行、也可在后端进行。

而单点登录要解决的问题就是,这个可供互信系统共同使用的登录凭证如何让多个互信系统都能跨域获取到,并让非法站点无法跨域获取到。

互信是对用户登录凭证的互信,需要能获取到互信应用站点的用户凭证,否则互信没有任何意义。

本文认为,统一的授权中心是单点登录的前提,全文都基于已实现统一授权中心的基础上。关于如何实现统一的授权中心也许可以借鉴 Oauth 的方案,其实通过 JWT 就已经可以很轻松实现授权中心了。

二、单点登录的方案比较

不同方案有不同的适用场景,各有优劣。

2.1 通过共享cookie实现

这是最简单的一种单点登录实现方式,用户在授权服务上进行登录,使用 cookie 作为媒介存放用户登录凭证,当用户访问互信的应用系统时携带上这个 cookie,应用拿到 cookie 进行校验,从而实现跨应用登录。

适用场景:

互信的子应用都在同一个主域名下,分别使用相同或者不同的子域名,在 cookie 存储时指定 domain 为二级域名,可以便捷的实现互信应用的 cookie 共享,从而达到单点登录的目的。

优点:

  1. 实现简单,开发工作量低;
  2. 由于 cookie 在请求时可以自动携带,单点登录非常丝滑,不需要先跳转授权中心的界面获取和验证凭证,直接就可以获取到凭证。

缺点:

  1. 限制为同一个主域名,对于复杂的场景不可用;
  2. 安全性较低,容易存在登录凭证被跨域获取的问题,即 Cross-site request forgery,简称 CSRFXSRF (跨站请求伪造);
  3. 登录凭证通过 cookie 自动携带,在访问应用前没有进行凭证的校验。

关于安全性,有方案说可以在存储前对 cookie 存储的登录凭证进行加密处理。个人认为有一定的效果,但是用处不大,因为就算加密了之后还是需要通过 JavaScript 解密然后发给服务端,JavaScript 解密这个过程是可分析的、不安全的,而且 cookie 在请求时自动携带的内容变为了无效数据。

如果不解密直接发送给服务端的话,加密也没有意义,恶意用户也无须关心凭证内容,直接发送给服务端请求就可以获取用户数据。

案例:

在调研单点登录方案时,稍微分析了百度的单点登录方案,百度就是通过共享 cookie 方案实现的单点登录,共涉及到 BDUSSBDUSS_BFESS 两个 cookie 项,为了安全性其都设置为了 HttpOnly,禁止了 JavaScript 访问凭证。

百度通过cookie记录登录状态

可以看到 BDUSSBDUSS_BFESS 都存储在了 .baidu.com 这个主域名下,所有的例如 tieba.baidu.comzhidao.baidu.com 等百度系的产品在请求时都会自动携带上这两个登录凭证,从而实现单点登录。这全程都是自动的,所以百度的这些产品单点登录都非常丝滑,无须界面上的跳转和点击重新授权等操作。

限制就是,百度的应用都在 baidu.com 这个主域名下,如果不在主域名下这个方案就不可行了。

2.2 通过页面重定向实现

互信的应用系统先从自己的 localStore 中尝试获取登录凭证,获取失败跳转授权中心的界面让用户登录,登录通过后携带登录凭证重定向到应用登录跳转地址,应用的登录跳转页面进行访登录凭证的存储,最后再跳转实际要访问的界面。

重定向的地址需要做校验,避免随意一个跳转地址都可以正常重定向而造成登录凭证泄露。

适用场景:

互信的子系统不在同一个主域名下。

通过 Oauth 实现单点登录采用的大都是这种方式。

优点:

  1. 解决了共享 cookie 方案需要同一主域名的限制;
  2. 不会自动携带登录凭证,避免 CSRF 问题;
  3. 可以在跳转时针对不同应用系统做安全性校验,发放不同的登录凭证,某个应用系统出现凭证泄露等安全问题,不影响互信的其他应用系统。

缺点:

体验不好,用户就算已经登录过其他互信系统了,在第一次访问还是需要进行一次跳转,无法丝滑的实现单点登录。

案例:

这种单点登录方式可对照 Oauth 的授权码模式,只是相比于 Oauth 来说更加的简单,用户无须进行授权。

应用系统跳转到授权服务的界面,授权服务验证用户登录状态,通过后发放给应用系统一个登录票据(类似于 Oauth 的授权码),应用系统拿着登录票据获取获取登录凭证并存储,最后跳转要访问的地址。

访问请求的地址 > SSO验证界面检验登录 > 应用系统登录跳转页面获取凭证 > 访问请求的地址

如上流程,共经过了 3 次的界面上的跳转,整体流程还是比较长的。

2.3 通过内嵌iframe实现

互信的系统先从自己的 localStore 中尝试获取登录凭证,获取失败后跳转应用系统自己的登录界面,通过 iframe 打开授权中心的界面让用户登录,用户登录成功后通过 iframe postMessage 传送登录凭证到应用系统,应用系统的登录页面进行访登录凭证的存储,最后再跳转实际要访问的界面。

适用场景:

互信的子系统不在同一个主域名下。

优点:

  1. 用户感受上仅跳转到登录界面,相比于与页面重定向方式体验更加的友好;
  2. 能够自定义登录页面。

缺点:

  1. 需要授权服务开发提供给 iframe 访问的界面,且需要和应用系统适配,复杂性较高。