单点登录(服务端):https://blog.csdn.net/qq_34997906/article/details/97007709
1. 缘起
为什么要把客户端单独拿出来写呢 ?
博主也参考了网上很多写单点登录的,但基本上都是大同小异,在客户端的自身权限校验 和 单点退出 均未做处理,显然并不满足实际的业务开发。
2. 核心流程
客户端登录:用户访问客户端,客户端 security 发现此请求的用户未登录,于是将请求重定向到服务端认证,服务端检测到此请求的用户未登录,则将此请求跳转到服务端提供的登录页面(前后端分离则是前端登录地址,否则为服务端内置的登录页面),登录成功后,服务端将系统的权限信息(为了减轻服务端的访问压力)和用户的特有标志(如用户名,记录此用户的登录状态)存入redis,然后服务端会跳回到用户第一次访问客户端的页面。
客户端URL的拦截:每次请求到来时,客户端都去Redis中去取认证中心存入的权限信息和用户特有的登录标志,权限信息只是为了匹配此登录用户是否有权利访问此接口,用户的特有标志则是为了检测该用户是否在其他客户端退出了,如若没有取到,则重定向到服务端的登录页面。
3. 所需依赖
1 | <!-- 集成 SSO 依赖 --> |
4. 配置介绍
4.1 security 核心配置
1 |
|
配置说明:
.withObjectPostProcessor(urlObjectPostProcessor());
此配置表示启用了spring-security
的自定义校验,要实现URL的自定义校验,核心就是urlFilterInvocationSecurityMetadataSource
,urlAccessDecisionManager
这两个类,第一个类主要功能是 拿到 访问 此URL所需要的GrantedAuthority
(即 需要哪些角色),第二个类主要功能是比较用户有的GrantedAuthority
(用户拥有的角色)是否包含此URL需要的GrantedAuthority
(角色组),只要有一个匹配上则允许访问,没有匹配上则表示没有权限。
4.2 自定义 FilterInvocationSecurityMetadataSource 的配置
1 | /** |
为什么返回
ROLE_LOGIN
?ROLE_LOGIN
,见名知意,只需要登录即可访问,最后返回只是为了给系统没有纳入权限表的URL加一层校验,当然,你也可以直接返回null,这样没有匹配上的URL访问将不受security的访问限制。
4.3 自定义 AccessDecisionManager的配置
1 | "urlAccessDecisionManager") ( |
4.4 用户未登录时的处理
1 | /** |
配置说明
当在其他客户端退出清掉redis中数据时,此处会产生循环重定向无法跳转到登录页面的问题,我这边的处理是,当前端因为循环重定向拿不到响应时,就直接前端跳转到登录页面,重新登录,不知大家有没有更好的方式。
4.5 用户没有权限时的处理
1 | /** |
ResponseUtils封装的是一些返回的JSON信息,包含跨域的请求头等。
5. yml中 客户端的配置
1 | auth-server: http://192.168.1.201:9999 // 认证中心的地址 |
6. Controller
1 | 4j |
关于获取登录用户信息
因为是OAuth
客户端访问服务端,所以一定得带上服务端给颁发的access_token
才能在服务端拿到用户数据,否则服务端无法识别,将标识此次请求为未登录,关于yml
中userInfoUri
的配置,我也有点疑惑,官方文档也没有给出咋使用。
测试
单点登录测试:
单点退出测试:
参考博客
https://www.baeldung.com/sso-spring-security-oauth2
https://www.linzepeng.com/2018/10/31/sso-note1/