网络基础11 单点登录

单点登录SSO(Single Sign On)就是在一个多系统共存的环境下,用户在一处登录后,就不用在其他系统中登录,也就是用户的一次登录能得到其他所有系统的信任。

实现单点登录就是要解决的几个关键问题:

  1. 存储信任
  2. 验证信任
  3. 销毁信任

通过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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
"nick": "****",
"sso": [
"//sso.jd.com/setCookie?t=sso.jcloud.com&callback=?",
"//sso.jd.com/setCookie?t=sso.jd.hk&callback=?",
"//sso.jd.com/setCookie?t=sso.yiyaojd.com&callback=?",
"//sso.jd.com/setCookie?t=sso.jdpay.com&callback=?",
"//sso.jd.com/setCookie?t=sso.baitiao.com&callback=?",
"//sso.jd.com/setCookie?t=sso.jdwl.com&callback=?",
"//sso.jd.com/setCookie?t=sso.toplife.com&callback=?",
"//sso.jd.com/setCookie?t=sso.tjjt360.com&callback=?",
"//sso.jd.com/setCookie?t=sso.jdcloud.com&callback=?",
"//sso.jd.com/setCookie?t=sso.paipai.com&callback=?",
"//sso.jd.com/setCookie?t=sso.jkcsjd.com&callback=?"
],
"info": "<a href=\"//home.jd.com\" target=\"_blank\" class=\"link-user\">****</a>&nbsp;&nbsp;<a href=\"https://passport.jd.com/uc/login?ltype=logout\" class=\"link-logout\">退出</a>"
}

(3)首页的JS脚本会遍历SSO这个对象,对每个地址发送跨域的JSONP登陆请求(例如:https://sso.jd.com/setCookie?t=sso.jdcloud.com&callback=?):

(4)请求的响应结果中没有实质的响应对象,头信息的响应码是302,所以浏览器会根据返回的响应头中的Location字段中的新地址重新发送请求

注意,Location后面附加了一个参数参数cc应该是服务端根据登陆信息经过加密后的数据,与生成登陆凭据cookie有对应关系

之所以要通过302跳转的方式生成这个c,我推测是因为加密算法的私钥不能泄露在客户端,所以必须有服务端来生成加密后的数据,再通过Locationc带给目标的域名,这样一来保证了加密算法都统一维护在了sso这个服务端的代码下

(5)浏览器根据的Location字段中的新地址重新发送跨域请求:

返回的响应头中含有Set-Cookie选项,这样就在https://sso.jcloud.com域名下写入了登陆信息相关的cookie

1
2
3
SET-COOKIE: thor=3D19BE10EEAFA435CF3B318D409CB1E9E7ADC5BF43A9E5D59FA90B017918A5F6FB2323D13F4B408E9C050C911C4EBD06A6B61A5044F9E33135DF1A3D1A52317EC1BC5E0CC182D72826FA6587C266563B662299AC793759F548AF005AA7EA339BED10D4BE400DEE9BEB264DEE3599F3CF78B9C0F96C053BF12521B94D5B95AA39CF9290AF5D572663BC15433FBA48099A;path=/;domain=.jdcloud.com;httponly
Set-Cookie: pin=****; Domain=.jdcloud.com; Path=/
Set-Cookie: unick=****; Domain=.jdcloud.com; Path=/

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,详细过程如下:

关键点:

  1. Client1返回302,页面跳转到CAS Server时会携带参数service,指明要登录的子系统
  2. 用户输入用户名密码后,由CAS Server生成Ticket,跳转回Client1时在URL中返回Ticket
  3. Client1经重定向跳转后获取到Ticket,向Server发送请求校验Ticket的真实性
  4. 校验通过后Server返回登录名,并跳转到Client1
  5. 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时的不同主要在于:

  1. 访问Client2时,由于没有Client2对应的JESSIONID,所以证明Client02并没有建立会话
  2. 重定向到CAS Server时,会携带上TGT的cookie,CAS server由此判断用户已经登陆,所以用户不必再次输入用户名和密码,直接发送Ticket
  3. 其余过程与时登陆Clinet1基本相同

(2)统一注销

当用户在浏览器中点击“注销”链接后,浏览器会访问CAS Server的注销页面(会携带上TGT的cookie),CAS Server会读取TGT,安检查当前用户登陆过的所有Client,并依次发送注销请求

CAS Client收到注销请求后,会根据URL中的Ticket,读取Session,将对应的Seesion删除、注销:

参考