共计 2847 个字符,预计需要花费 8 分钟才能阅读完成。
什么是单点登录?
单点登录,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不共享几种方案:
- 系统间做Session同步,就是有一个系统Session更新,就立刻复制到其他系统,缺点是影响性能
- 根据请求的IP做IP Hash映射到对应的机器上,就相当于同一个请求IP会一直访问同一台服务器,缺点是这台服务宕机了,就会丢失一部分Session数据
- 把Session数据抽离出来放在redis中,比较推荐
把登录功能单独抽取出来,做成一个系统,其他系统在登录时,请求SSO(登录系统)进行登录,将返回的token写到cookie中,下次访问将cookie带上。
技术实现:
- SSO系统生成一个token,并将用户信息存到Redis中(原来存在Session),并设置过期时间
- 其他系统请求SSO系统进行登录,得到SSO返回的token,写到Cookie中
- 每次请求,Cookie都会带上,拦截器拿到token,到SSO系统验证token的有效性
Cookie跨域问题
关于跨域的问题,参考这篇文章:https://www.20zyn.cn/什么是跨域?
比如有两个系统A和B,域名分别是a.service.com,b.service.com,用户在系统A(a.service.com)登录后,系统A返回给浏览器的Cookie,用户在请求系统B的时候是不能带过去的。这个例子是由于子域名不同产生的跨域
几种解决方案:
- 服务端将Cookie写到客户端后,客户端对Cookie进行解析,将token解析出来,此后请求把这个token带上即可
- 多个域名共享Cookie,服务端需要设置客户端Cookie的domain
- 将Token保存在SessionStroage中,不依赖Cookie就没有跨域的问题了
- 使用CORS,CORS需要浏览器和服务器同时支持,目前几乎所有浏览器都支持,IE版本不能低于10,前端在携带Cookie时,需要将withCredentials设置为True,配置完全后端设置。
- 使用jsonp跨域只能使用get请求
CAS原理
CAS也是单点登录一种常用的解决方案,全称是Central Authentication Service,认证中心。
在下面的时序图里,我模拟了三个服务,CAS服务,系统A服务,系统B服务,分别部署在cas.com,systemA.com,systemB.com
这里搭配 JWT,分5个场景来讲解CAS。
场景1:用户第一次访问业务系统(systemA.com/page1)
关键点:
- 流程梳理: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,验证通过,返回数据
- 3次重定向:第一次:发现客户端未登录,让其重定向到CAS;第二次:CAS验证通过了客户端提交过来的表单数据,生成session,签发JWT,通知客户端重定向到systemA的验证地址;第三次:systemA解析url中jwt,向CAS验证通过后,通知客户端重定向到最初访问的地址
- JWT的Cookie是写在systemA域下面的,所以每次访问systemA都能把jwt带过来
- sid的Cookie是写在cas.com的域下面的,所以重定向到CAS就能把sid带过去
- CAS服务的session是CAS服务创建的,CAS做集群部署的时候,要考虑sessionId的唯一性和共享性
- session是有生命周期的,到期自动销毁,不需要做session的管理
场景2:用户登录系统A后,继续访问systemA.com/page2
登录系统A后,客户端每次访问就能带上JWT的Cookie,所以其实就是上图中的第15步开始后的流程,只不过地址变成systemA.com/page2
场景3:登录系统A后,再去访问系统B(systemB.com/page1)
关键点:
- 因为登录了系统A,说明已经和CAS建立过会话,所以访问B后,重定向CAS做验证时,会携带sid的Cookie,这时候就不要用户再次填写登录信息了
场景4:登录系统B后,继续访问systemB.com/page2
登录系统B后,客户端每次访问同样能携带系统B的JWT的Cookie
场景5:退出登录
这里假设在系统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攻击