## SSO
我们知道如果要登录系统,就是需要输入用户名与密码,由服务器来做验证,验证通过之后就建立session, 然后把session id通过cookie发给人类的浏览器,下次人类再访问URL的时候,会把cookie一并传过来,这样系统就知道他们登录过了。
此时如果一个公司有多个系统,对于同一个用户,可能密码还不一样,能不能**在一个地方登录一次,就可以访问所有的系统了?**
有人可能会说,登录其实就是cookie,如果把cookie共享起来不就可以了?
事实上没那么简单 ,因为cookie是不能**跨域**的,a.com产生的cookie,浏览器是不会发到b.com上去,所以共享cookie方案不管用。
不过对于同一个公司内部的系统,完全可以统一到一级或者二级域名之下, 比如 xxx.company.com,
但是在登录前后端没有保存session,所以当cookie发过来之后,内存没有数据,无法知道之前是否登录过,当然谈不上验证。
![](http://p8a6vmhkm.bkt.clouddn.com/picgo20181023142346.png?picgo)
那么既然有共享cookie,那么session是否可以共享呢?
众多系统, 架构不同,语言也不同, 共享 session 太麻烦
## token
我们可以同样借助于cookie,比如用户在报销系统登录了,可以在cookie中写个token,用户访问别的系统的时候,就把token带过去,那个系统验证一下token,如果没问题,就认为它已经登录了。
![](http://p8a6vmhkm.bkt.clouddn.com/picgo20181023142756.png?picgo)
每个系统在生成token的时候,需要对数据做个签名,防止篡改。
![](http://p8a6vmhkm.bkt.clouddn.com/picgo20181023143422.png?picgo)
- 生成签名:通过header与userID,使用hash算法和密钥生成签名,并且把这个签名作为token数据一部分
- 收到token,再利用相同算法计算签名,与发过来的相比,如果相等,说明登录过,直接取出userID即可。
这里面的问题在于算法和密钥大家得一致,而且密钥的分发可能存在风险。
而且token里面放了一个userID,但是每个系统的userID都不一样,拿过来没有任何用。
## 统一认证中心
可以建立一个统一认证中心,所有用户注册和认证都在这里做。
那么认证中心如何通知每个系统用户已经认证呢?
比如用户先访问系统`www.a.com/pageA`,如果系统发现用户没有登录,需要把它**重定向**到认证中心[www.sso.com/login?redirect=www.a.com/pageA](www.sso.com/login?redirect=www.a.com/pageA)
> redirect后面的url是表示,认证通过之后,还需要重定向回来
浏览器放访问到了认证中心,认证中心就会让用户去登录,登录成功之后,认证中心需要:
- 建立一个session
- 创建一个ticket(随机字符串)
- 再重定向到系统A处,url带着ticket:[www.a.com/pageA?ticket=T123](www.a.com/pageA?ticket=T123), 同时cookie也会发给浏览器,比如set cookie: ssoid=1234, sso.com
你可能会说,这个cookie对系统A没有任何用处,因为跨域不能访问?
cookie是sso.com的,所以肯定没有用,浏览器只是会保存下来。
但是ticket完全可以用来向认证中心再次做验证,主要作用是防止有人伪造。
![](http://p8a6vmhkm.bkt.clouddn.com/picgo20181024094104.png?picgo)
然后浏览器会再次访问[www.a.com/pageA?ticket=T123](www.a.com/pageA?ticket=T123), 然后系统A拿着token,去问认证中心,如果认证中心说这个他们发的,就可以认为用户在认证中心登录过了。
此时系统A就为浏览器建立session,返回pageA这个资源。
同时,系统A还需要给浏览器返回一个cookie,这是属于系统A的cookie(之前的是SSO的cookie)
```
set cookie:sessionid=xxx,a.com
```
> 注意,浏览器实际有两个cookie,一个是系统A发的,一个是认证中心发的
如果用户要再访问系统A的另一个受保护的页面,[www.a.com/pageA1](www.a.com/pageA1) , 还需要去认证中心登录吗?
不用,因为系统A已经给浏览器发过自己的cookie,所以浏览器会把这个cookie带过来,这样就知道它登录过了。
![](http://p8a6vmhkm.bkt.clouddn.com/picgo20181024094940.png?picgo)
如果用户访问www.a.com/pageA 时已经通过认证中心登录了,那么再访问www.b.com/pageB 会发生什么状态?
和访问www.a.com/pageA 非常类似,唯一不同的是不需要用户登录了,因为浏览器已经有了认证中心的cookie, 直接发送给www.so.com 即可。
![](http://p8a6vmhkm.bkt.clouddn.com/picgo20181024095132.png?picgo)
同样,认证中心会返回token,www.b.com 需要做验证
![](http://p8a6vmhkm.bkt.clouddn.com/picgo20181024095502.png?picgo)
所以,本质上就是一个认证中心的cookie加上多个子系统的cookie而已。
SSO是单点登录,所以还需要有单点退出。用户在一个系统退出来,认证中心需要把自己的会话和cookie干掉,然后当需要通知各个系统,让他们把会话统统干掉,才能实现真正的退出。