网络基础11 单点登录
单点登录SSO(Single Sign On)就是在一个多系统共存的环境下,用户在一处登录后,就不用在其他系统中登录,也就是用户的一次登录能得到其他所有系统的信任。
实现单点登录就是要解决的几个关键问题:
- 存储信任
- 验证信任
- 销毁信任
通过cookie实现
只而通过cookie作为媒介实现单点登录是最简单的一种实现方法。用户登录父应用后,父应用返回一个加密的cookie(放在根域名下),当用户访问子应用的时候会带上这个cookie。
子应用会对这个cookie进行解密和校验,校验通过则登录当前用户。
这种方法存在两个问题,第一个是cookie可能被泄露,虽然可以加密,但是仍有一定风险。第二个问题时不能实现跨域登录,只能在父子应用之间实现。所以这种方法一般不会单独使用。
通过JSONP实现
在父应用中登陆后,父应用将带有登陆信息的cookie会存到客户端的父应用域名下。
当用户需要登陆子应用的时候,授权子应用访问父应用提供的用于验证登陆信息的JSONP等接口,子应用获得授权后会向父应用发起跨域请求,并带上父应用域名下的cookie。
父应用接收到请求后,验证cookie,获取用于的登陆状态,返回加密的信息给子应用,子应用通过解析返回来的加密信息来专验证用户,如果通过验证则会登陆用户。
这种方式能够解决跨域登陆的问题,但是安全性也有一定隐患,如果加密算法被泄露,那么攻击者可以在本地按照加密算法来伪造父应用的响应请求。子应用受到响应后一样可以通过验证登陆用户。
通过页面重定向实现
通过父应用和子应用来回重定向进行通信,实现信息的安全传递。父应用提供一个GET方式的登陆接口,用户通过子应用重定向的方式访问这个接口。如果用户没有登陆,则返回登陆页面,用户输入账号密码进行登陆。如果已经登陆了,则生成加密的Token,并且重定向到子应用的验证Token的接口,通过解密和校验后,子应用登陆当前用户。
这种方式的安全性更高,并且可以解决跨域登陆的问题,但是复杂性相对较高。目前采取这种方式实现单点登陆的例子已经非常多了,
京东的单点登陆实现
京东的单点登陆是通过跨域设置cookie实现SSO单点登录(2018)。
(1)首先点击登录按钮,页面跳转至京东登录中心,https://passport.jd.com/new/login.aspx?ReturnUrl=https%3A%2F%2Fwww.jd.com%2F
, 登录之后验证通过跳转至returnUrl
对应的地址,即京东首页
(2)首页通过jQuery的getJSON
方法发起对https://passport.jd.com/loginservice.aspx
(2019.10此API更新为https://passport.jd.com/new/helloService.ashx?pin=****&uuid=****&callback=jsonpHelloService&_=****
)的跨域请求,获取需要跨域设置登录cookie的页面列表,返回值是JSON数据
1 | { |
(3)首页的JS脚本会遍历SSO这个对象,对每个地址发送跨域的JSONP登陆请求(例如:https://sso.jd.com/setCookie?t=sso.jdcloud.com&callback=?
):
(4)请求的响应结果中没有实质的响应对象,头信息的响应码是302
,所以浏览器会根据返回的响应头中的Location
字段中的新地址重新发送请求
注意,Location
后面附加了一个参数参数c
,c
应该是服务端根据登陆信息经过加密后的数据,与生成登陆凭据cookie有对应关系
之所以要通过302
跳转的方式生成这个c
,我推测是因为加密算法的私钥不能泄露在客户端,所以必须有服务端来生成加密后的数据,再通过Location
将c
带给目标的域名,这样一来保证了加密算法都统一维护在了sso
这个服务端的代码下。
(5)浏览器根据的Location
字段中的新地址重新发送跨域请求:
返回的响应头中含有Set-Cookie
选项,这样就在https://sso.jcloud.com
域名下写入了登陆信息相关的cookie
1 | SET-COOKIE: thor=3D19BE10EEAFA435CF3B318D409CB1E9E7ADC5BF43A9E5D59FA90B017918A5F6FB2323D13F4B408E9C050C911C4EBD06A6B61A5044F9E33135DF1A3D1A52317EC1BC5E0CC182D72826FA6587C266563B662299AC793759F548AF005AA7EA339BED10D4BE400DEE9BEB264DEE3599F3CF78B9C0F96C053BF12521B94D5B95AA39CF9290AF5D572663BC15433FBA48099A;path=/;domain=.jdcloud.com;httponly |
cookide的domain
设置为了.jcloud.com
,表明该cookie可以被*.jcloud.com
共享
(6)小结:
如果A域名和B域名(A与B的一级域名不同,因为二级域名是可以共享cookie的)要共享登陆状态,如果A域名已经登陆的状态下,B域名下的cookie是通过A向B发送跨域请求,由B将cookie写在自己的域名下
A向B发送的跨域请求中包含了经过加密的登陆信息,当A/B/C/D都设置了同意的加密凭据,也就做到了“单点登陆”的要求。至于如何对加密凭据进行解析则是经过了特定算法,由一个专门的后台服务(上面是/sso.jd.com/setCookie
)进行处理。
CAS登陆原理
CAS是一个单点登录框架, 是Yale大学发起的一个开源项目,旨在为Web应用系统提供一种可靠的单点登录方法,代码目前在github上管理。
假设有两个CAS Client应用,一个CAS Server,各个应用的Session信息是不相同的,在浏览器中名为JSESSIONID的cookie信息都是各不相同的。某一个应用,无法读取其他应用的cookie信息。
(1)第一次访问Client1
用户打开浏览器后第一次访问Client1,页面会重定向到登陆页面,用户需要输入账号密码登陆。登陆成功后,跳转回Client1,详细过程如下:
关键点:
- Client1返回302,页面跳转到CAS Server时会携带参数
service
,指明要登录的子系统 - 用户输入用户名密码后,由CAS Server生成
Ticket
,跳转回Client1时在URL中返回Ticket
- Client1经重定向跳转后获取到
Ticket
,向Server发送请求校验Ticket的真实性 - 校验通过后Server返回登录名,并跳转到Client1
- Client1获取登录名后写入Request和Session,将JSESSIONID写入cookie(建立会话),并且将全局登陆信息TGT写入CAS Server域名(或者根域名下的)cookie
TGT是CAS Server为每一个登录用户创建的登录令牌。在CAS Server上拥有了TGT,用户就可以证明自己在CAS Server成功登录过。TGT封装了SessionCookie值以及此Cookie值对应的用户信息。当HTTP请求到来时,CAS以此Cookie值为key查询缓存中有无TGT ,如果有的话,则相信用户已登录过。
当用户已经访问过CAS Client后,当用户再次访问,系统不会再跳转到CAS Server做认证。
(2)用户第一次访问Client2
当用户已经登陆系统,切换到另一个CAS Client时,无序再次输入账号密码,过程如下:
这里面的与第一次访问Client1时的不同主要在于:
- 访问Client2时,由于没有Client2对应的JESSIONID,所以证明Client02并没有建立会话
- 重定向到CAS Server时,会携带上TGT的cookie,CAS server由此判断用户已经登陆,所以用户不必再次输入用户名和密码,直接发送Ticket
- 其余过程与时登陆Clinet1基本相同
(2)统一注销
当用户在浏览器中点击“注销”链接后,浏览器会访问CAS Server的注销页面(会携带上TGT的cookie),CAS Server会读取TGT,安检查当前用户登陆过的所有Client,并依次发送注销请求
CAS Client收到注销请求后,会根据URL中的Ticket
,读取Session,将对应的Seesion删除、注销: