文章目录
什么是SSO?
web登录的本质是什么?
如何增加状态/会话
单个系统的登录
扩展到多个系统,就是SSO
方案1:共享cookie,后台共享session
spring session项目就是共享session,共享session放到redis中
Session Cookie要种在Web应用的根域上,也就是说不同Web应用的根域必须相同,否则会有跨域问题。
方案2:共享cookie,后台不共享session
签名的方式来校验
SSO,要保存用户登录的会话关系。比如浏览器场景下,其是放到session中。而在方案2场景下,其将会话状态存放在客户端(浏览器上)。
方案3:使用独立的认证中心
CAS(central authentication service)
耶鲁大学发起的开源SSO
几个核心概念
- CAS Server / CAS Client
- Service
- TGT: Ticket Granting Ticket
- ST: Service Ticket
CAS Server:sso server,所有的登录认证都是由独立的认证中心完成
CAS Client:系统A 系统B
TGT:为每个用户创建了一个登陆令牌,用来证明用户已经在CAS上登陆了
下面以浏览器,系统A,系统B和认证中心为例,说明SSO的流程
1. 访问系统A(未登录)
为什么a.com不直接访问认证中心,而要通过302redirect的方式呢?因为浏览器要获取一个认证中心的session
在认证中心创建一个session
setCookie 并且ticket
2. 登录系统A(已登录)
系统A要去人认证中心验证,这个ticket是sso.com发出的。
其是通过一个全局的cookie和多个子系统的cookie来实现的
3. 登录了系统A以后,再来访问系统B
返回给系统B的ticket与系统A的ticket不同,但代表的是同一个用户
4. 访问系统B(已登录)
此时就有三个cookie了
认证中心会把系统A和系统B注册,每次的ticket都是不同的
5. 退出登录
向认证中心发送一个注销请求,其需要查看这个用户注册过系统A和系统B,把这三个session全部注销掉。认证中心调用系统A的logout接口,将session invalidate掉,那么系统A中应该记录了session与用户相关的信息,可以标识出这是哪个用户的session,将相对应的session invalidate掉就可以了。
可以思考下如何进行全局退出?
对比起来jwt的无状态的优点就显示出来了
6. 再思考下CAS
系统A一定要去认证中心验证ticket吗?
saml
4和5之间也是要走浏览器的
问题:系统A如何对认证中心发送的消息做认证
系统A如何得到SSO server的公钥呢?
实际中是系统A导入SSO server的证书
service provider:系统A
identity provider:认证中心
与前面的CAS最大的不同就是系统A验证用户是否在sso server登陆的方式
- cas使用ticket方式,系统A需要把ticket给sso server来验证
- saml使用xml+签名(RSA) security access mark language,系统A不需要和sso server进行验证交互了
问题:saml这种方式如何注销会话呢?因为注册没有了?
回答:这种方式类似jwt,想要注销jwt令牌通常会有两种做法:
- 将每次生成JWT令牌时的秘钥粒度缩小到用户级别,也就是一个用户一个秘钥。这样,当用户取消授权或者修改密码后,就可以让这个密钥一起修改。一般情况下,这种方案需要配套一个单独的密钥管理服务。
- 在不提供用户主动取消授权的环境里面,如果只考虑到修改密码的情况,那么我们就可以把用户密码作为JWT的密钥。当然,这也是用户粒度级别的。这样一来,用户修改密码也就相当于修改了密钥。
CAS和saml都适合在企业内部使用,不适合互联网
OIDC (openID Connect)
场景:
有些App不想非常麻烦地自己设计一套注册和登录认证流程,就会寻求统一的解决方案,然后势必会出现一个平台来收揽所有类似的认证登录场景。再反过来理解也是成立的。如果有个拥有海量用户的、大流量的访问平台,来提供一套统一的登录认证服务,让其他第三方应用来对接,不就可以解决一个用户使用同一个账号来登录众多第三方App的问题了吗?而OIDC,就是这样的登录认证场景的开放解决方案。
OIDC的三个主要角色
- EU(End User),代表最终用户。
- RP(Relying Party),代表认证服务的依赖方,就是第三方软件。
- OP(OpenID Provider),代表提供身份认证服务方。
现在很多App都接入了微信登录,那么微信登录就是一个大的身份认证服务(OP)。一旦我们有了微信账号,就可以登录所有接入了微信登录体系的App(RP),这就是我们常说的联合登录。
一个用户G要登录第三方软件A,A有三个子应用,域名分别是a1.com、a2.com、a3.com。如果A想要为用户提供更流畅的登录体验,让用户G登录了a1.com之后也能顺利登录其他两个域名,就可以创建一个身份认证服务,来支持a1.com、a2.com和a3.com的登录。
有了第一次登录,服务端有了登录状态,会做相应的判断,就不会让用户再次输入用户名和密码了。那个步骤3是在未登录的情况下发生的。已登录的情况下没有步骤3的。
在保证用户身份认证功能的前提下,如果想获取更多的用户信息,就再通过访问令牌ACCESS_TOKEN获取。在OIDC框架里,这部分内容叫做创建UserInfo端点和获取UserInfo信息。
OIDC的流程就是:生成ID令牌->创建UserInfo端点->解析ID令牌->记录登录状态->获取UserInfo。
问题:如果退出?
我觉得退出就是访问OP,取消已登录状态。客户端尽管有ID_TOKEN,但是其已经失效了
基于JWT实现SSO
JWT就是用一种结构化封装的方式来生成token的技术。结构化之后,令牌本身就可以被“塞进”一些有用的信息。其具有一种“自编码”的能力。
注意,JWT跟OAuth 2.0或者OIDC等并没有直接关系,它只是一种结构化的信息存储,可以被用在除了OAuth 2.0或者OIDC以外的任何地方。比如,重置密码的时候,会给你的邮箱发送一个链接,这个链接就需要能够标识出用户是谁、不能篡改、有效期5分钟,这些特征都跟JWT相符合。也就是说,JWT并不是OAuth 2.0协议或OIDC等规范所涵盖的内容。
浏览器实现
一个关键的点是:
应用1在第2步是要认证并授权的,在相同的session下,应用2在第9步时并不需要认证了,只要授权就可以了
实际上,client2请求oauth/authorize的时候,JSESSIONID与client1请求 oauth/authorize的时候(应该是登录认证通过之后)的JSESSIONID相同,于是认证服务器就识别出此用户已经通过了登录认证,直接跳到授权页
上面这种方式是利用将登陆会话保存到session中的,应用1和应用2返回的JWT令牌是不同的,但是都代表同一个用户
相比共享一套JWT令牌,这种方式更好
app实现
如果说我们应用A是淘宝手机客户端,应用B是天猫手机客户端
那么如何实现SSO呢?
主要的区别就是认证服务器如何识别出当我登录天猫手机客户端的时候,不需要登录认证了?本质就是要保持登录这个会话状态
浏览器中是靠cookie/local storage中的JSESSIONID
那么在app中,
思路1:加一个类似OIDC ID_TOKEN,用来标识用户,将其保存起来,标识用户登录状态。
思路2:其可以在app(客户端)上通过共享JWT令牌保存会话关系信息。