什么是单点登录?

单点登录,Single Sign On,简称SSO

是一种控制多个相关但彼此独立的系统的访问权限,拥有这一权限的用户可以使用单一的ID和密码访问某个或多个系统从而避免使用不同的用户名或密码,或者通过某种配置无缝地登录每个系统。

一、单系统登录机制

http无状态协议

http是基于TCP协议之上的高级通信协议,是无状态的,浏览器的每一次请求,服务器会独立处理,不会跟之前的或之后的请求产生关联,每一次请求之间没有任何联系。

要鉴别浏览器的请求,就必须清楚浏览器的请求状态,所以要服务器和浏览器共同维护一个状态,这就是会话机制

会话机制

浏览器第一次请求服务器,服务器会创建一个会话(session),并将会话ID作为响应的一部分发送给浏览器,浏览器存储会话ID,在后续请求带上会话ID,服务器取出请求中的会话ID就能知道是不是同一个用户了。

cookie机制就是浏览器用来存取少量数据的机制,数据是key-value形式。在tomcat服务器中,会话ID存储在cookie中,key为"JSESSIONID"。

会话机制

登录状态

  • 登录:将用户信息保存在Session对象中,客户端请求带过来的sessionId能查到对象,说明已登录,查不到说明没登录或已退出
  • 注销:从Session中删除用户的对象
  • 记住登录状态:配合Cookie使用

二、多系统单点登录

Session不共享

单系统的登录功能主要使用Session来保存用户信息,但是多系统就有多个Tomcat,而Session依赖于当前系统的Tomcat,所以系统间的Session是不共享的。

解决Session不共享几种方案:

  1. 系统间做Session同步,就是有一个系统Session更新,就立刻复制到其他系统,缺点是影响性能
  2. 根据请求的IP做IP Hash映射到对应的机器上,就相当于同一个请求IP会一直访问同一台服务器,缺点是这台服务宕机了,就会丢失一部分Session数据
  3. 把Session数据抽离出来放在redis中,比较推荐

把登录功能单独抽取出来,做成一个系统,其他系统在登录时,请求SSO(登录系统)进行登录,将返回的token写到cookie中,下次访问将cookie带上。

技术实现:

  1. SSO系统生成一个token,并将用户信息存到Redis中(原来存在Session),并设置过期时间
  2. 其他系统请求SSO系统进行登录,得到SSO返回的token,写到Cookie中
  3. 每次请求,Cookie都会带上,拦截器拿到token,到SSO系统验证token的有效性

Cookie跨域问题

关于跨域的问题,参考这篇文章:https://www.20zyn.cn/什么是跨域?

比如有两个系统A和B,域名分别是a.service.com,b.service.com,用户在系统A(a.service.com)登录后,系统A返回给浏览器的Cookie,用户在请求系统B的时候是不能带过去的。这个例子是由于子域名不同产生的跨域

几种解决方案:

  1. 服务端将Cookie写到客户端后,客户端对Cookie进行解析,将token解析出来,此后请求把这个token带上即可
  2. 多个域名共享Cookie,服务端需要设置客户端Cookie的domain
  3. 将Token保存在SessionStroage中,不依赖Cookie就没有跨域的问题了
  4. 使用CORS,CORS需要浏览器和服务器同时支持,目前几乎所有浏览器都支持,IE版本不能低于10,前端在携带Cookie时,需要将withCredentials设置为True,配置完全后端设置。
  5. 使用jsonp跨域只能使用get请求

CAS原理

CAS也是单点登录一种常用的解决方案,全称是Central Authentication Service,认证中心。

在下面的时序图里,我模拟了三个服务,CAS服务,系统A服务,系统B服务,分别部署在cas.com,systemA.com,systemB.com

这里搭配 JWT,分5个场景来讲解CAS

场景1:用户第一次访问业务系统(systemA.com/page1)

用户第一次访问

关键点:

  1. 流程梳理:1).客户端访问systemA.com/page1,发现未登录,重定向到CAS;2).CAS验证通过后生成Session,签发JWT,写入sid的Cookie;3).客户端把JWT当做url参数重定向到systemA做验证; 4).systemA拿着JWT去CAS验证有效性,验证通过,设置jwt到Cookie,通知客户端再次重定向到systemA.com/page1; 5). 客户端请求systemA.com/page1,systemA再次向CAS验证客户端Cookie里的jwt,验证通过,返回数据
  2. 3次重定向:第一次:发现客户端未登录,让其重定向到CAS;第二次:CAS验证通过了客户端提交过来的表单数据,生成session,签发JWT,通知客户端重定向到systemA的验证地址;第三次:systemA解析url中jwt,向CAS验证通过后,通知客户端重定向到最初访问的地址
  3. JWT的Cookie是写在systemA域下面的,所以每次访问systemA都能把jwt带过来
  4. sid的Cookie是写在cas.com的域下面的,所以重定向到CAS就能把sid带过去
  5. CAS服务的session是CAS服务创建的,CAS做集群部署的时候,要考虑sessionId的唯一性和共享性
  6. session是有生命周期的,到期自动销毁,不需要做session的管理

场景2:用户登录系统A后,继续访问systemA.com/page2

登录系统A后,客户端每次访问就能带上JWT的Cookie,所以其实就是上图中的第15步开始后的流程,只不过地址变成systemA.com/page2

用户登录系统A后继续访问其他页面

场景3:登录系统A后,再去访问系统B(systemB.com/page1)

登录系统A后,再访问系统B

关键点:

  • 因为登录了系统A,说明已经和CAS建立过会话,所以访问B后,重定向CAS做验证时,会携带sid的Cookie,这时候就不要用户再次填写登录信息了

场景4:登录系统B后,继续访问systemB.com/page2

用户登录系统B后继续访问其他页面

登录系统B后,客户端每次访问同样能携带系统B的JWT的Cookie

场景5:退出登录

这里假设在系统B退出

在系统B退出登录

关键点:

  • 删除cas.com域下面的sid的Session,jwt因为存在客户端,也不需要删除,因为cas在验证jwt时,会解析payload里的sid,发现Session已失效,同样会验证不通过

方案总结

  • 整个会话管理还是基于Session来做的,但是Session只存在于CAS服务
  • CAS之所以信赖JWT,是因为JWT也是CAS签发的
  • 使用JWT注意点:1.使用https;2.使用http-only的Cookie,针对sid和jwt;3.密钥不能丢;4.防范CSRF攻击