后端思想-单点登录组件的设计与思考

1,899 阅读10分钟

2023/09/13回顾点评--稳定运行了两三年的组件,现在还没出啥问题,只能说我当时干掉shiro自己写一个组件的想法太正确了。可惜也是没后续了,无需更新,主站稳定后就没有再动了,说实话,不是正经SSO。

前言

本文仅记录我在配合公司框架以及适配自己设计的门户网站的SSO组件开发历程。由于受到诸多限制,比如公司框架以及开发时间等因素,导致该单点登录组件并不是完整的认证授权鉴权三位一体。但是读者可从我的设计和思考中获得一些启发,本文详细记录了SSO组件五次大的变动,解释了很多疑问,比如SSO是依靠现有的shiro之类的中间件还是自研?兼容和扩展?目前该单点登录组件仍在第五版后继续优化中,已持续稳定运行一年。

PS:由于该组件极度受限,并不是一个优秀的SSO组件,因此本文不提供相关代码,并会隐藏公司相关信息。

公司x版本的SSO框架

image.png

如图,该SSO框架是未用任何市面上流行中间件,自写的一个校验方式。该方案的亮点在于插拔式的认证方式,极大的提升了扩展性,虽然这种扩展方式存在一定的门槛,但不妨碍这是一项优秀的设计。

框架中存在部分未完成代码,比如缓存以及指定登录方式,这部分没写完的不影响现有的功能实现。

该框架依赖于HR服务提供登录校验API,由各个组件提供自组件的登录校验API,轻依赖。

优点:

1.高扩展性

2.轻量,低耦合

缺点:

1.非OA登录时,循环校验带来的时间损耗

2.无缓存,存在缓存模块但未实现

PS:部门2.7.0版本以上提供sourceId认证后不提供后续授权以及鉴权服务

改造后单点登录

基于门户网站的考虑,决定整合登录方式,OA则单独进行校验

第一版于2021年4月28日定下

最初想法是整合所有登录包括OA、渠道、合作伙伴、供应商等等现有组件,因此放弃了以组件方式进行扩展的方案。技术选型的时候,使用了流行的轻型权限框架shiro,用shiro的目的是两点管理用户登录信息,权限注解开发。优点是登录有缓存,校验起来更快,shiro提供了大部分实用的API,集成方便,提供了权限注解,登录认证、权限认证,更方便做权限控制。

为什么没有选择spring security?

当时是听说很重,而且部门框架提供的spring boot版本是2.1.x版本太低,为了避免出现各种冲突或者别的问题就没有选择。而且当时第一版的时候考虑非常简单,就想用个简单,而且shiro我提前预研过,很方便,就选用了shiro。

截图.png

第二版的时候开始做了差不多一周于2021年5月13日总结定下

SCM的登录、SSO-BASE的改造已经写好。这个时候出于部门规范的考虑,SSO-OA不能动了,如果要保留原来的界面登录的,这就不受SCM项目控制了,因为页面的登录和SCM的登录是两个不同项目。

对第一版的设计做了如下优化:

  1. shiro部分做了细化,哪个部分加入缓存,如何做校验有了详细的设计。
  2. 登录后的操作做了细化,日志,更新登录用户表,缓存,注入本地变量,解析用户的IP、登录省市、浏览器、操作系统。
  3. SSO-BASE的解析规则变动,分为校验SCM登录的cookie和OA登录的cookie

截图.png

第三版于2021年5月17日定下并修改完毕

于第二版暴露了一个相当致命的问题,OA的登录不受shiro控制,从而导致了一个问题就是,OA登录的用户实际上没有在SCM集成的shiro中登录。

我一开始的想法是拦截器做模拟登录,但是shiro在模拟登录的方法也会优先校验是否登录,因为OA登录跳过了shiro提供的登录方法,再通过别的接口请求SCM的时候必然会进行登录校验。如果要修改这个规则的话,可以重写shiro的过滤器规则,不修改的话会造成一个问题,第一次请求的时候,shiro必定返回需登录的错误,但是拦截器在之后就会登录上shiro,第二次请求的时候就正常了,但很明显这种是不太行的。

本来我打算重写的,但是一想,shiro突然没有意义了

我为什么又要放弃集成shiro?

  1. 登录缓存,SCM在设计之初,针对OA之外的登录和认证都做了有效期为16小时的缓存。为了解决OA用户登录的问题,写了拦截器,判断是否为OA用户,OA用户是否存在缓存,判断OA用户IP是否发生变化,后面这两步都是为了减少登录后的数据库交互,提高认证效率。
  2. 权限注解开发,当初为了提供跨项目的权限控制,为其他集成新SSO的项目提供的注解,其实用在SCM和shiro的注解式开发差不多。

所以shiro的集成就显得臃肿了,与其强行适配,不如取消,全部改由自写。

截图.png

SSO-ZERO的优点

  1. 保留了原版的高扩展性,依旧可以使用组件进行扩展
  2. 大量使用缓存,提高登录及认证效率
  3. 权限由本地线程变量维护,可跨项目进行权限校验,并且提供权限注解式开发
  4. 整合登录(除了OA),并且用户登录可不选泽登录方式,系统循环所有集成登录方式进行校验,校验后为当前用户赋予登录方式,加速下次登录时认证速度。
  5. 不再依赖于部门SSO-BASE,能够高度自定义用户在各个项目中的信息传递

权限中心待优化点

1.权限修改时,及时变更缓存中的权限

SSO-ZERO待优化点

1.所有认证链接可配置,不强依赖于SCM

2.提供模拟用户身份切换的功能

第四版于2021年6月21日追加持续迭代到2021年10月27日封版1.2.8

判断逻辑增加uid的判断,该uid来源于SCM的用户表,因为userid在不同系统可能存在相同的名称,因此追加。

为什么不单单使用uid?

userid在部门OA中用于标识个人身份,在OA中是唯一的,在部门提供的方法中userid也是常用的参数,因此为了避免从缓存或者数据库二次查询,就一直保留userid

第五版于2021年10月28日开始开发SID版本到2022年1月4日封版1.4.9

部门推行SID,但是只提供认证,不提供授权以及鉴权了。因此不用再兼容OA,所有的授权和鉴权都将由SCM和SSO-ZERO控制,整体设计再次简化。

截图.png

第六版于2022年8月24日封版

紧急需求,前后约耗时两天开发完毕。背景是需要强行兼容另一种鉴权,两个系统使用的不同的SSO,但是鉴权载体都是Cookie,那就还是比较好做了。

目标是从另一个系统跳转到当前系统时,不会弹出登录页面,而是需要解析另一个系统的Cookie(同一域名)自动登录到当前系统,就不用用户再次登录了。

首先是向另一方的开发询问解析Cookie(以下简称新Cookie)里加密数据的方法,了解到具体的解密方法后就可以拿到userid。然后开始改造组件,组件依旧是优先处理自己的Cookie(以下简称老Cookie)。在老Cookie和续约Cookie都不存在的时候,开始解析新Cookie,获取到解密后的userid,拿着userid向门户网站请求获取该用户的权限和基本信息,创建老Cookie以及提前注入ThreadLocal,因为当前请求是真实请求了,需要获取用户信息。从别的系统跳转到当前系统时,需要在真实请求之前模拟登录门户。

image.png

改造过程版更

1.4.0初步完成SID版本改造,SCM及SSO-ZERO组件完成适配
1.4.11.移除无用登录校验2.整合用户信息及前端菜单、权限、组等信息接口,合二为一,减少远程调用次数,加速鉴权3.增加SSO日志4.删除冗余代码,精简代码
1.4.21.所有用户鉴权操作融合成一个接口,减少校验次数2.新增测试环境的cookie,并做好正式测试的隔离3.优化包结构,简化代码
1.4.3追加错误定位日志
1.4.41.新增渠道登录校验2.日志BUG,API部分优化
1.4.6排除XX平台账号中间空格数据导致URL解析失败BUG FIX
1.4.91.错误码修改2.增加续约token
1.5.1接入部门新网关,解决新老网关兼容问题
1.5.3加入实用工具类CurrentUserUtil,用于获取PTM角色,组等信息
1.6.4解决phplogin兼容问题
....

设计总结与思考

开发这个SSO组件,其实也没有从头开始做,因为考虑到兼容的问题,基本上就是在原框架的基础上进行改建。设计思路也是在第三版左右定型,此后就是不断的优化,整个设计其实相当粗糙,刨除了认证模块的SSO组件是不完整的,受限于内网的环境也没有考虑跨站点等情况。

  • 核心的授权用的最普通的cookie,因为二级域名都一样,所以cookie其实也凑合,如果跨站点会考虑使用Token(比如JWT)。
  • 加密用的AES,其实用框架jaspyt加密更好。
  • 为了优化用户体验,后面追加了refresh token功能,做了正式和测试环境的隔离。
  • 留了SPI的扩展位,但是目前只有我在维护这个组件,所以这个扩展位暂时也没大用
  • 用户信息用了threadlocal来存储信息,异步的话会丢失信息,考虑加入阿里的TTL来解决线程信息传递的问题

现在回首再看这个组件,比较感慨,这是我从头开始设计的第一个组件,遇到也解决了很多问题,从这开始开发新的组件就变得如鱼得水非常easy。这也是我目前更新频率最低的组件,一方面是核心组件不想乱动,二十几个系统都在用,乱动怕出问题,二是受限于场景,即使优化了,也未必能有用到的场景,时间性价比不高。如果让我挑刺,这肯定是个不合格的SSO模块,但是配合公司现在的情况也是够用了。

更新记录

2022/08/26

  1. 新增第六版,主要是兼容了外系统的鉴权
  2. 重绘流程图