这一篇是原理篇,接下来还会有一篇实战篇,实战的相关代码是非常火的一个开源项目叫:xxl-sso
一、简介
单点登录(Single Sign On),简称为 SSO。
它的解释是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。
所谓一次登录,处处登录。同样一处退出,处处退出
。
二、背景
在我们企业发展初期的时候,企业内部使用的系统都会比较少,一般也就一个或者两个,每个系统有自己的登录功能。运营人员将自己的账号登录还是很方便。
但是随着公司的发展,公司的系统越来越多,比如有OA系统、CRM系统、财务管理系统、设备管理系统等,这个时候总不能每个系统都登录一遍吧,那真的会崩溃的。
合理做法是用户只需要登录一次就可以访问所有相互信任的应用系统。
三、回顾下单系统登录是怎么样的?
我们都知道,http是无状态的协议,这意味着当你登录成功后请求其它接口服务端也并不知道你之前登录过。那怎么办呢?
这个时候我们会想到Cookie
+Session
组合来解决http无状态问题。
如果说Cookie
是检查用户身上的”通行证“来确认用户的身份,那么Session
就是通过检查服务器上的”客户明细表“来确认用户的身份的。
那这里完整的登录流程应该是这样的:
1)、 首次登录验证成功之后,后端会将用户信息存在Session对象中。
2)、同时设置 Set-Cookie 字段,并把 SessionId 等信息写入进去,并设置过期时间,这些信息就是 Cookie。浏览器会保存这些 Cookie 信息
3)、之后在请求该系统其它接口的时候,因为是同域名,浏览器会自动在请求头上添加 Cookie 字段,并带上保存的 Cookie 信息。
4)、后端接受到请求后,会在请求头中取出 SessionId的值,然后再去服务器上的Session获取对于的用户信息,如果获取成功说明登录验证成功了,不需要再重复登录。
总结
根据以上流程可知,SessionID 是连接 Cookie 和 Session 的一道桥梁,大部分系统也是根据此原理来验证用户登录状态。
所以,一般我们单系统实现登录会这样做:
-
登录:将用户信息保存在Session对象中
-
- 如果在Session对象中能查到,说明已经登录
- 如果在Session对象中查不到,说明没登录(或者已经退出了登录)
-
注销(退出登录):从Session中删除用户的信息
四、多系统登录会存在的一些问题?
我们说单系统中登录流程实现关键点在于 Cookie
和 Session
的配合使用,但在多系统中就会存在很明显的两个问题
- 在多系统情况下服务端
Session
不共享。 - 在多系统情况下客户端
Cookie
不共享(跨域)。
如果能解决这两大难点,那实现多系统登录就简单多了
1.为什么会存在Session不共享?
我们说Session是存储在服务端
的。
比如说现在有3台Tomcat服务器,当我们访问第1台Tomcat时,我们是可以将用户信息存在第1台Tomcat的Session中,但当我们访问第2台Tomcat的时候,这台服务器是
没有对应的Session数据,这就是所谓的Session不共享问题。
2、如何解决session共享问题呢?
说如何解决session共享问题呢,其实就是如何解决服务端数据共享问题
我们常见有3种解决方案:
第一种方案就是session拷贝
。
当某一台Tomcat对session中的信息进行了修改都会同步给其他Tomcat,这样session就可以共享。
这种方案有三大缺陷
- 每一台服务器都会存储一份完整的session,增加服务器端压力也会浪费内存。
- 因为涉及到服务之间的同步,所以可能存在延迟。
第二种方案就是不通过session共享数据,而是采用redis
。
redis纯天然解决了session不能共享的问题,而且redis除了存储查询效率高以外,还支持数据持久化功能,不用担心数据会丢失。
第二种方案也是现在现在企业级使用最多的一种方案。
第三种采用JWT
。
我们在使用session或者使用redis,前端cookie其实只是存了个key,我们还需要拿着这个key到服务端的session,或者redis或者Mysql,总之都需要查一遍,但如果是JWT,
它最大的特点就是这个JWT本身就含有用户信息,服务端只要解析这个JWT成功,就可以获取用户信息。
3、为什么会Cookie跨域问题?
本质:由于浏览器安全策略,cookie只能在同一域名产生和使用
比方说,我们在请求www.a.com的时候,浏览器会自动把www.a.com的Cookie带去服务端。
但我们在请求www.b.com的时候,是不会把www.a.com下的Cookie带到b服务器的。
这就意味着由于域名不同,用户向系统A登录后,系统A返回给浏览器的Cookie,用户再请求系统B的时候不会将系统A的Cookie带过去。
至于如何解决Cookie跨域问题,不在这篇文章的讨论范畴内,下面实现单点登录的方式也不是通过解决Cookie跨域来实现的。
五、单点登录原理
相比于单系统登录,sso需要一个独立的认证中心,只有认证中心能接受用户的用户名密码等安全信息,其他系统不提供登录入口,只接受认证中心的间接授权。
1、SSO应用核心设计
应用系统
:OA系统、CRM系统(需要登录的系统)
SSO客户端
:登录、退出(独立jar包给应用系统引用)
SSO服务端
:登录(登录服务)、登录状态(提供登录状态校验/登录信息查询的服务)、退出(用户注销服务)
数据库
:存储用户账户信息(一般使用Mysql)
缓存
:存储用户的登录信息(一般使用Redis)
2、SSO登录流程
对于这个流程图,我看网上问的最多的一个问题就是
根据同源策略:只要 协议+域名+端口号 一个不同,那么就不能进行跨域。www.oa.com 和 www.crm.com 域名都不相同了。也就是www.crm.com是
拿不到www.oa.com中cookie中的token的,那crm.com在请求的时候为什么不需要登录呢?
其实这个问题,上面的流程图已经很清楚了。它也并不是通过解决跨域问题来实现单点登录的。
它实现的核心原理在于:
个人用户请求www.oa.com时,因为oa.com的cookie下没有token信息,所以跳转到sso.com/login,因为是第一次登录,所以sso.com的cookie下也没有token信息,所以需要
用户输入账号密码登录,登录成功会在sso.com域名下保存token信息,同时会把token信息返回给oa.com。
这样oa.com和sso.com下的cookie都有token信息。
而第一次访问crm.com的时候,它下面是没有token信息,所以会跳转到sso.com/login进行登录,但因为sso.com域名下cookie已经有token信息,所以不用再输入账号密码信息
直接把token返回到crm.com就可以,这个过程用户是无感知的,所以也就实现了一次登录处处登录了。
3、sso注销流程
对于这个流程,问的比较多的是: oa.com退出登录了。如何做到让crm.com也需要重新登录的?
通过上面的流程图我们可以知道www.oa.com退出登录,只能去除oa.com
和sso.com
域名下cookie下的token,但是crm.com域名下的cookie还是可以获取token的,
那能获取就代表这可以正常访问www.crm.com的接口了吗?
其实不是的,因为我们还有校验token有效性这一步(令牌校验),我们拿着这个token去redis获取用户信息,其实已经获取不到了,因为上面退出登录的时候已经清除了,
所以令牌校验失败一样要重新登录。
声明: 公众号如需转载该篇文章,发表文章的头部一定要 告知是转至公众号: 后端元宇宙。同时也可以问本人要markdown原稿和原图片。其它情况一律禁止转载!