一、单点登录介绍
1.1 什么是单点登录
单点登录(Single Sign On),简称SSO,是一种身份验证机制,允许用户使用一个凭据登录多个应用程序。用户只需要提供凭据一次,就可以访问多个应用程序。
是目前比较流行的企业业务整合的解决方案之一
SSO一般都需要一个独立的认证中心(passport),子系统的登录均得通过 passport,子系统本身将不参与登录操作
当一个系统登录成功以后,passport 将会颁发一个令牌给各个子系统,自胸膛可以拿着令牌会获取各自的受保护资源,为了减少频繁认证,各个子系统在被 passport 授权以后,会建立一个局部会话,在一定时间内可以无需再次向passport发起认证。
简单的讲,就是在一个系统共存的环境下,用户在一处登录,即可访问所有相互信任的系统。就像健康保一样,为你的身份做担保
举个例子,比如淘宝、天猫都属于阿里旗下的产品,当用户登录淘宝后,再打开天猫,系统便自动帮用户登录了天猫,这种现象背后就是用单点登录实现的。
1.2 单点登录的背景
企业内,员工使用的系统很多,每个系统都有自己的登录账号密码,这样就要求员工记住很多的账号密码,并且每次都要登录,这样很麻烦,面临着诸多挑战:
- 应用太多,员工账号密码众多,复杂南极,或设置单一存在安全隐患
- 登录入口众多,没有统一界面,切换频繁,用户使用体验查
- 不同应用和员工数据之间割裂,不同设备和操作系统不兼容
- 不同应用、各地分支机构,档案信息不同
1.3 单点登录的优点
-
提高用户的效率
用户只需要登录一次,就可以访问所有相互信任的应用系统,减少了登录的频率,提高了工作效率
-
提高开发人员的效率
用户只需要记住一个账号密码,就可以访问所有相互信任的应用系统,减少了记忆的负担
-
简化管理
用户在访问A应用系统时,无需再次登录,即可访问B应用系统,提高了用户体验
1.4 单点登录的缺点
-
可用性很低
因为一旦单点登录系统挂掉,所有的应用系统都将无法访问
-
安全性要求很高
一旦单点登录系统被攻破,所有的应用系统都将面临被攻破的风险
-
不利于重构
因为涉及到所有相互信任的应用系统,如果有应用系统需要重构,就需要重构所有的应用系统
-
无人看守桌面
因为只需要登录一次,所有的授权的应用系统都可以访问,可能导致信息泄露
1.5 单点登录的应用场景
- 企业内部的应用系统,如:企业OA、企业邮件、企业办公等
- 企业对外的应用系统,如:企业官网、企业商城、企业服务等
- 企业对外的第三方应用系统,如:微信公众号、支付宝生活号等
二、单点登录的实现
要实现单点登录,需要先将登录认证功能单独抽取出来,变成一个独立的SSO认证中心,进行登录认证和权限控制,而其它子系统不提供登录认证功能,只提供资源访问接口,子系统只需要通过SSO认证中心验证即可访问资源。
当用户访问子系统受限资源时,如果判断未登录,都跳转到认证中心进行登录认证。
2.1 父域cookie(基于Cookie的SSO单点登录)
使用cookie作为媒介,存放用户凭证,这是最简单的单点登录实现方式。
【拓展】:
- cookie的作用域由
domain属性和path属性共同决定
domain属性的有效值为当前域或其父域的域名/IP地址path属性的有效值是以“/”开头的相对路径- cookie还有一个特点,父域中的cookie被子域所共享(子域会自动继承父域中的cookie)
- 具体实现:
用户登录父应用之后,应用返回一个cookie,当用户访问子应用的时候,携带上这个cookie,授权应用cookie并进行校验,校验通过登录当前用户。
-
缺点:
-
cookie不安全
可以通过加密cookie保证安全想,当然这是在源代码不泄露的前提下
-
不能跨域实现免登录
不同域下的cookie无法共享
-
不适合移动端
-
-
适用场景:
同域名下系统的单点登录。如 tieba.baidu.com 和 map.baidu.com,它们都建立在 baidu.com这个主域名之下,那么它们就可以通过这种方式来实现单点登录
2.2 认证中心
我们可以部署一个认证中心,认证中心就是一个专门负责处理登录请求的独立的web服务
用户统一在认证中心进行登录,登录成功后,认证中心记录用户的登录状态,并将 Token 写入 Cookie
【注意】:这个cookie 是认证中心的,应用系统是访问不到的
-
具体实现:
-
应用系统检查当前请求有没有 Token。如果没有,说明用户在当前系统中尚未登录,那么就将页面跳转至认证中心。
由于这个操作会将认证中心的 cookie 自动带过去,因此,认证中心能够根据 Cookie 知道用户是否已经登录过了。
-
如果认证中心发现用户尚未登录,则返回登录页面,等待用户登录。如果认证中心发现用户已经登录过了,就不会让用户再次登录了,而是会跳转会目标URL,并在跳转前生成一个Token,拼接在目标URL的后面,回传给目标应用系统
-
应用系统拿到Token之后,还需要向认证中心确认下 Token 的合法性,防止用户伪造。
确认无误后,应用系统记录用户的登录状态,并将Token写入cookie,然后给本次访问放行
【注意】:这个cookie 是本应用系统的,应用系统是访问不到的
当用户再次访问当前应用系统时,就会自动带上这个 Token,应用系统验证 Token 发现用户已登录,于是就不会有认证中心什么事了。
-
这里顺便介绍两款认证中心的开源实现:
-
Apereo CAS 是一个企业级单点登录系统,其中 CAS 的意思是”Central Authentication Service“。它最初是耶鲁大学实验室的项目,后来转让给了 JASIG 组织,项目更名为 JASIG CAS,后来该组织并入了Apereo 基金会,项目也随之更名为 Apereo CAS。
-
XXL-SSO 是一个简易的单点登录系统,由大众点评工程师许雪里个人开发,代码比较简单,没有做安全控制,因而不推荐直接应用在项目中,这里列出来仅供参考。
-
优点:
- 支持跨域
- 扩展性好
- 安全性好
-
缺点:
- 实现方式相对复杂
- 需要额外的服务器资源
2.3 LocalStorage 跨域
可以价格 SessionID或Token 保存到浏览器的 LocalStorage 中,让前端再每次向后端发送请求,主动将 Localstorage 的数据传递给服务端
-
具体实现:
- 用户在应用系统A登录成功后,将 SessionID 或 Token 保存到 LocalStorage
- 应用系统B 检查 LocalStorage 中是否有 SessionID 或 Token
- 如果有,就将其作为请求头信息发送给后端
- 后端检查请求头信息中的 SessionID 或 Token 合法性,合法就放行
这些都是由前端来控制,后端需要做的仅仅是在用户登录成功后,将 sessionID 或 Token 放在响应体中传递给前端
单点登录完全可以在前端实现。前端拿到
SessionID或Token后,除了将它们写入自己的Localstorage中之外,还可以通过特殊手段将它写入到多个其它域下的Localstorage中关键代码入下:
// 获取 token var token = result.data.token; // 动态创建一个不可见的iframe,在iframe中加载一个跨域HTML var iframe = document.createElement("iframe"); iframe.src = "http://app1.com/localstorage.html"; document.body.append(iframe); // 使用postMessage()方法将token传递给iframe setTimeout(function () { iframe.contentWindow.postMessage(token, "http://app1.com"); }, 4000); setTimeout(function () { iframe.remove(); }, 6000); // 在这个iframe所加载的HTML中绑定一个事件监听器,当事件被触发时,把接收到的token数据写入localStorage window.addEventListener('message', function (event) { localStorage.setItem('token', event.data) }, false);前端通过 iframe+postMessage() 方式,将同一份 Token 写入到了多个域下的 LocalStorage 中,前端每次在向后端发送请求之前,都会主动从 LocalStorage 中读取Token并在请求中携带,这样就实现了同一份Token 被多个域所共享
此种实现方式完全由前端控制,几乎不需要后端参与,同样支持跨域
-
优点
- 实现方式相对简单,几乎不需要后端参与
- 支持跨域
- 浏览器会自动管理 LocalStorage,不容易被篡改
-
缺点
- 安全性不高,因为 LocalStorage 可以被用户读取到
- 如果用户清除浏览器缓存,就会导致用户需要重新登录
三、单点登录流程
3.1 登录
- 用户访问系统A的保护资源,系统A发现用户未登录,跳转至认证中心,并将自己的地址作为参数传过去
- SSO认证中心发现用户未登录,将用户引导至登录界面
- 用户输入用户名密码提交登录申请
- SSO认证中心校验用户信息,创建用户与SSO认证中心之间的会话,称为全局会话,同时创建授权令牌
- SSO认证中心将用户重定向至系统A,并将授权令牌作为参数传递
- 系统A接收到授权令牌,并将令牌与SSO认证中心校验令牌是否有效
- SSO认证中心校验令牌有效,则系统A创建用户与系统A之间的会话,称为局部会话
- 用户访问系统A的保护资源,系统A校验局部会话有效,则响应用户请求(返回受保护资源)
- 用户访问系统B的受保护资源
- 系统B发现用户未登录,跳转至SSO认证中心,并将自己的地址作为参数
- SSO认证中心发现用户已登录,跳转回系统B,并将令牌作为参数
- 系统B拿到令牌,去SSO认证征信校验令牌是否有效
- SSO认证中心校验零头,返回有效,注册系统B
- 系统B使用该令牌创建于用户的局部会话,返回受保护资源
用户登录成功后,会与SSO认证中心及各个子系统建立会话,用户与SSO认证征信建立的会话被称为全局会话。用户与各个子系统建立的会话称为局部会话。
局部会话建立后,用户访问子系统受保护资源将不再通过SSO认证中心,全局会话与局部会话有以下约束关系:
- 全局会话与局部会话的生命周期相同
- 全局会话的失效会导致局部会话失效(全局会话销毁,局部会话必须销毁)
- 全局会话的有效期不应超过局部会话的有效期(局部会话存在,全局会话不一定存在)
- 局部会话的有效期不应超过全局会话的有效期(全局会话存在,局部会话不一定存在)
3.2 注销
SSO认证征信一直监听全局会话的状态,一旦全局会话销毁,监听器将通知所有注册系统执行注销操作
- 用户向系统A发起注销请求
- 系统A根据用户与系统A建立的SessionID拿到令牌,向SSO认证中心发起注销请求
- SSO认证中心校验令牌有效,销毁全局会话,同时取出所有永此令牌注册的系统地址
- SSO认证中心向所有注册系统发起注销请求
- 各个注册系统接受SSO认证中新的注销请求,销毁局部会话
- SSO认证中心引导用户至登录页面
四、单点登录的协议
4.1 CAS协议
CAS(Central Authentication Service)的缩写,中央认证服务,一种独立开放指令协议,用于为多个应用程序提供单点登录服务。
-
CAS组合:
- CAS Service。需要独立部署,主要负责对用户的认证工作
- CAS Client。负责处理对客户端受保护资源的访问请求,需要登录时,重定向到 CAS Service
-
CAS协议的流程如下:
- 用户向客户端发起请求,客户端将用户重定向至认证中心,并将服务地址作为参数
- 用户在认证中心输入用户名密码,认证中心校验用户信息,创建TGT(Ticket-Granting Ticket),并将TGT保存在缓存中
- 认证中心将TGT作为参数,重定向回客户端,客户端拿到TGT后,将TGT作为参数,请求ST(Service Ticket)
- 客户端将ST发送给服务端,服务端将ST发送至认证中心,认证中心校验ST,并将ST与对应的TGT进行绑定
- 服务端接收到ST后,向认证中心发起请求,校验ST与TGT的绑定关系,并将ST标记为已消费
- 服务端接收到认证中心的响应后,允许用户访问资源
4.2 OAuth协议
OAuth(Open Authorization)的缩写,开放授权协议,是一个开放标准,允许用户让第三方应用访问该用户在某一网站上存储的私密的资源,而无需将用户名和密码提供给第三方应用。
通俗说,OAuth就是一种授权的协议,只要授权方和被授权方遵守这个协议去写代码提供服务,那双方就是实现了OAuth模式。
详细说就是,OAuth在"客户端"与"服务提供商"之间,设置了一个授权层(authorization layer)。"客户端"不能直接登录"服务提供商",只能登录授权层,以此将用户与客户端区分开来。"客户端"登录授权层所用的令牌(token),与用户的密码不同。用户可以在登录的时候,指定授权层令牌的权限范围和有效期。"客户端"登录授权层以后,"服务提供商"根据令牌的权限范围和有效期,向"客户端"开放用户储存的资料。
OAuth2是OAuth1.0的下一个版本,OAuth2关注客户端开发者的简易性,同时为Web应用,桌面应用和手机,和起居室设备提供专门的认证流程。原先的OAuth,会发行一个 有效期非常长的token(典型的是一年有效期或者无有效期限制),在OAuth 2.0中,server将发行一个短有效期的access token和长生命期的refresh token。这将允许客户端无需用户再次操作而获取一个新的access token,并且也限制了access token的有效期。
- OAuth协议的流程如下:
- 用户访问客户端,客户端将用户重定向至认证服务器,并将客户端的回调地址作为参数
- 用户在认证服务器输入用户名密码,认证服务器校验用户信息,创建AT(Access Token),并将AT保存在缓存中
- 认证服务器将AT作为参数,重定向回客户端,客户端拿到AT后,将AT发送给服务端
- 服务端接收到AT后,向认证服务器发起请求,校验AT
- 认证服务器接收到请求后,校验AT,并将AT标记为已消费
- 认证服务器将校验结果返回给服务端,服务端接收到响应后,允许用户访问资源
4.3 JWT协议
JWT(JSON Web Token)的缩写,是一个开放标准,它定义了一种紧凑且独立的方式,可以在各方之间作为JSON对象安全地传输信息。此信息可以通过数字签名进行验证和信任。JWT可以使用秘密(使用HMAC算法)或使用RSA或ECDSA的公钥/私钥对进行签名。
- JSON WEB令牌结构由三部分组成:
- Header(头部):包括令牌的类型及正在使用的散列算法。
- ayload(负载):声明是关于实体(通常是用户)和其他数据的声明。索赔有三种类型:标准注册声明,公共的声明和私有的声明。
- Signature(签名):必须采用编码标头,编码的有效负载,秘密,标头中指定的算法,并对其进行签名。