单点登录SSO
简介
单点登录,英文:Single Sign On,简称SSO。
是什么?有什么用?
一个大型系统一般都会有多个子系统,用户访问每个子系统的受限资源都需要进行登录鉴权,如果不实现单点登录,那用户访问每个子系统都得登录一下,就非常的麻烦。
另一个例子,假设一家公司有非常多的产品,用户用每个产品都得登录一次是不是非常的麻烦,体感很不好,倘若能做到在一个产品上登录了,也能直接使用其他产品,这种丝滑的体验将极大提升用户的舒适度。
总结:
单点登录是指用户在一个系统上登录了,其他系统也能感知到用户已登录,而不需要重复登录。
一处登录,到处使用
作用:
当一家公司建立起了自己的产品生态,用户在一个产品上登录了,就能直接使用其他产品,而无须再次登录,极大提升了用户体验。
单系统登录常见方案介绍
平常自己做的项目一般都是一个系统,所以首先谈谈单系统登录
方案一:Cookie+Session,实现有状态登录。
首先,
cookie是服务端产生的数据,然后会返回给客户端保存。
session是服务端创建的一个对象,用来保存和客户端的会话信息。
在客户端请求服务端时,若服务端创建了session对象,会给客户端返回一个会话cookie(一般就是sessionId),然后客户端的后续请求都会携带这个会话cookie,服务端就可以根据请求携带的会话cookie找到对应的session对象,利用这个session对象可以保存和查询会话信息。
服务端创建session对象的时机是啥?
由服务端自己决定,可以在客户端第一次请求时就创建,也可以在用户登录时才创建...
大致思路:
用户登录成功后,服务端保存登录状态到session中,后续请求时服务端就检查session中的登录状态从而判断用户是否已登录。
该方案在用户登录后,会在服务端保存登录状态(用于后续请求时判断用户是否已登录) ,所以是有状态登录(可以理解成在服务端留痕了就是有状态登录)。
方案二:JWT实现无状态登录。
首先,JWT可以封装很多信息,比如:用户信息。
大致思路:
用户登录成功后,服务端会生成一个token并返回给客户端,token一般会封装当前用户信息,后续客户端请求时都会携带这个token,然后服务端会验证token是否合法,合法则说明用户登录了。
该方案用户登录后不会在服务端保存登录状态(也就是不会留痕去用于后续请求时判断用户是否已登录) ,所以是无状态登录。
总结
Cookie+Session登录方案的思想是什么?
就是服务端给客户端分配一个唯一标识(即sessionId),并且服务端会为其创建一个容器保存会话信息(即session对象),然后客户端后续请求都会携带这个唯一标识表示自己的身份,服务端通过这个唯一标识就可以获取对应的会话信息。
其实,这个唯一标识可以不是sessionId,存储会话信息的也可以不是session对象。参考cookie+session思想,我们可以在用户登录成功后给客户端颁发一个token作为唯一标识,并在服务端为其创建一个容器来保存该用户的会话信息,用户后续请求时也携带这个token表示自己的身份。
实现单点登录的思路
先自己思考下如何实现单点登录,有啥思路
如果使用有状态登录:
关键在于用户在一个系统上登录后,这个登录状态得保存到一个公共的地方(比如,数据库、redis等),然后其他系统都能访问。
大致思路:
用户在一个系统上登录成功后,服务端给客户端生成一个唯一标识用于标识用户身份,并保存登录状态到一个公共的地方,然后用户访问其他系统时会携带这个唯一标识,其他系统拿这个唯一标识去判断该用户的登录状态。
如果使用无状态登录:
关键在于所有系统生成JWT和验证JWT的方案得统一。
大致思路:
用户在一个系统上登录成功后,服务端生成JWT并返回给客户端,用户在访问其他系统时会携带这个token,其他系统会验证token是否合法从而判断用户是否已登录。
总结:
无论是使用有状态登录还是无状态登录,所有系统都得实现相同的登录和认证逻辑,这套逻辑其实可以抽出来单独作为一个模块,然后所有系统都依赖这个模块实现登录和认证;甚至可以抽出一个服务,所有系统都通过该服务实现登录和认证。
单点登录可能遇到的问题及解决方案
session不共享
用户在系统A上登录后,其session信息是存储在系统A上的,系统B访问不到。
解决方案:
-
session复制
缺点:影响集群性能
-
ip hash,保证同一个ip的所有请求都打到同一个服务器上
缺点:若服务器宕机了,则会丢失该服务器存储的所有session信息
-
把session数据存储在一个公共数据源,如:redis
cookie跨域问题
Cookie是不能跨域的,就会有这种情况:
用户在系统A上登录后返回的cookie信息在请求系统B时不能携带过去。
解决方案:
- 使cookie在多个域名生效,可以设置cookie的domain。
- 不依赖cookie存储,可以将信息保存到sessionStorage中。
CAS方案(Central Authentication Service)
即认证中心
思想:就是单独抽出一个服务,所有系统都通过该服务实现登录和认证,该服务维护用户的全局登录状态
假设现在有系统A、系统B两个业务系统和CAS认证中心服务。
用户访问系统A受限资源的流程:
- 系统A发现用户未登录,重定向到CAS服务,并将自己的地址作为参数(用于重定向回来)
- CAS服务发现用户未登录,则将用户引导至登录页面进行登录,用户输入用户名和密码进行登录,登录成功后会与CAS建立全局会话(CAS会生成一个token作为cookie返回给客户端)
- 登录成功后,重定向回系统A,会携带token,系统A拿到token会请求CAS进行认证,CAS认证成功后,用户会和系统A建立局部会话,此时用户在系统A上就是已登录状态。
如图:
此时,用户再访问系统B受限资源的流程:
-
用户访问系统B,系统B发现用户未登录,于是重定向到CAS,并将自己的地址作为参数(用于重定向回来)
由于之前已经将CAS返回的token作为cookie保存了,因此,此时重定向到CAS时会携带token
-
CAS拿到请求携带的token,发现该用户已经登录了,会直接重定向回系统B,并会携带token,系统B拿到token后会请求CAS进行认证,CAS认证成功后,用户会和系统B建立局部会话,此时用户在系统B上就是已登录状态。
如图:
CAS官方给出的登录流程
可以看到和上面介绍的流程是一模一样的
单点登出方案介绍
有登录就有登出,实现了单点登录也得考虑单点登出
单点登出的关键点:要销毁全局会话和所有局部会话。
大致思路:
- 用户在系统A上请求登出
- 系统A重定向到CAS进行登出(请求CAS时会携带对应的cookie)
- CAS会销毁全局会话,并且回调所有建立了局部会话的业务系统进行登出从而销毁所有局部会话,至此就完成了登出
那怎么知道要回调哪些业务系统呢?(其实就是得知道哪些业务系统建立了局部会话)
业务系统建立局部会话前都得请求CAS认证token,所以CAS肯定是能知道的,看自己如何实现。
细节问题
-
登录成功后,CAS如何建立全局会话?
本质就是单系统登录,方案有:
- 直接使用Cookie+Session方案;
- 参考cookie+session思想。 登录成功后,CAS给客户端分配一个唯一标识(如token,可存储在cookie中),并且CAS保存该客户端的登录状态。
- 使用JWT实现无状态登录。
-
如何从CAS重定向回业务系统,并携带token?
业务系统重定向到CAS时要将自己的地址作为参数用于重定向回来,然后CAS登录或认证成功后,重定向回原URL,并将CAS生成的token拼接到url上传给业务系统。
为什么要给业务系统携带token?
就是为了告诉业务系统该用户已经登录了,然后业务系统就和用户建立局部会话。
为什么业务系统还要请求CAS认证token?
因为token可能伪造。
-
业务系统要如何获取CAS返回的token,每个业务系统都要编写相同的代码去获取token吗?(这样不仅代码冗余,而且还对业务系统有侵入)
需要搞一个sso-client,每个业务系统都得依赖sso-client(相当于提供一个sdk),sso-client负责获取token,请求CAS(即sso-server)认证等一系列通用逻辑
-
业务系统请求CAS认证成功后,业务系统如何建立局部会话?
本质也是单系统登录,方案有:
- 直接使用cookie+session方案
- 参考cookie+session思想。 业务系统给客户端分配一个唯一标识(如token2,可存储在cookie中),并且业务系统保存该客户端的登录状态。
- 使用JWT实现无状态登录
总结
CAS方案的本质:
其实就是通过一个公共服务,即认证中心,来维护用户的全局登录状态。所有系统都通过认证中心进行登录和认证,登录或认证通过后认证中心会给业务系统颁发token表示该用户已登录,然后业务系统就和用户建立局部会话。
核心要点:
-
CAS服务维护用户的全局登录状态,所有系统都通过CAS服务实现登录和认证
-
CAS方案采用C/S架构,分为客户端和服务端,如:sso-client,sso-server
所有业务系统都必须集成sso-client,整个单点登录过程其实就是sso-client和sso-server交互的过程。
sso-client职责:
- 拦截业务系统未登录请求,跳转到认证中心
- 请求认证中心校验token
- 建立局部会话
- 拦截用户登出请求,然后请求认证中心进行登出
- 接收认证中心的登出回调,销毁局部会话
sso-server职责:
- 验证用户登录信息
- 创建全局会话
- 创建并返回token
- 校验sso-client的token
- 接收sso-client的登出请求,销毁全局会话,并回调sso-client销毁局部会话
-
登录成功后,与CAS服务建立全局会话;认证成功后,与当前系统建立局部会话。
局部会话的作用:不用每次认证都请求CAS服务进行认证。
全局会话和局部会话的约束关系:
- 局部会话存在,全局会话一定存在
- 全局会话存在,局部会话不一定存在
- 全局会话销毁,局部会话必须销毁
其实,和认证中心建立全局会话以及和业务系统建立局部会话就是单系统登录,而单系统常见登录方案有Cookie+Session和JWT,单系统一般有多实例,那就需要注意多实例之间如何共享登录状态(比如,采用cookie+session方案登录就要共享session或采用JWT实现无状态登录)
总之,CAS方案在一些细节的实现上有很多玩法。
开源实现参考
- Apereo CAS
- XXL-SSO