旧的Spring Security OAuth已停止维护,全面拥抱新解决方案Spring SAS
Spring Authorization Server 替换 Shiro 指引
背景
Spring 团队正式宣布 Spring Security OAuth 停止维护,该项目将不会再进行任何的迭代 目前 Spring 生态中的 OAuth2 授权服务器是 Spring Authorization Server 已经可以正式生产使用 作为 SpringBoot 3.0 的最新权限方案,JeecgBoot springboot3_sas分支,已经完成了采用Spring Authorization Server 替换 Shiro工作。
JeecgBoot SAS分支
Date: 2024-01-17 技术栈: SpringBoot3+ Spring Authorization Server+jdk18
源码下载:
后端:https://github.com/jeecgboot/jeecg-boot/tree/springboot3_sas 前端:https://github.com/jeecgboot/jeecgboot-vue3/tree/springboot3_sas
登录对接
jeecg 基于Spring Authorization Server扩展了四种登录实现,加上默认提供的四种,共计有8种登录方式,额外还有OpenID Connect模式。本文不讲解授权码模式、客户端模式、刷新码模式、设备码模式、OpenID Connect模式,只会讲解jeecg实际应用了的四种扩展模式,其它模式请查阅Spring Authorization Server官方原文。
https://docs.spring.io/spring-authorization-server/reference/overview.html
注意:OpenID Connect应当仅为认证阶段使用,不可作为权限校验阶段使用。
密码模式和APP模式
密码模式在Oauth2.1协议中被放弃,Spring Authorization Server并没有对该模式提供实现,该实现是基于Spring Authorization Server提供的扩展入口实现的。
密码模式实现源码:package org.jeecg.config.security.password;
APP模式实现源码:package org.jeecg.config.security.app;
密码模式与APP模式实现完全一致,不过防止额外需求偏差,所以进行了分开实现。
请求地址:{baseUrl} /oauth2/token
请求方法:POST
请求头:
请求头名称 请求头值 Authorization Basic base64(clientId:clientSecret)(此处需要自行替换) Content-Type application/x-www-form-urlencoded
请求参数:
参数名称 参数值 grant_type password/app (password为PC端使用,app为移动端使用) username 用户名 password 密码
响应内容:
参数名称 参数含义 access_token 访问token,在被限制访问的接口请求中添加Authorization: Bearer access_token refersh_token 刷新token,用于刷新码模式获取新的access_token userInfo 当前登录用户信息 ... 其它内容不作详解,请查看源码
phone模式
phone模式用于手机+验证码登录场景。
phone模式实现源码:package org.jeecg.config.security.phone;
请求地址:{baseUrl} /oauth2/token
请求方法:POST
请求头:
请求头名称 请求头值 Authorization Basic base64(clientId:clientSecret)(此处需要自行替换) Content-Type application/x-www-form-urlencoded
请求参数:
参数名称 参数值 grant_type 固定为phone mobile 手机号 captcha 验证码
响应内容:
参数名称 参数含义 access_token 访问token,在被限制访问的接口请求中添加Authorization: Bearer access_token refersh_token 刷新token,用于刷新码模式获取新的access_token userInfo 当前登录用户信息 ... 其它内容不作详解,请查看源码
social模式
任何一个用户中心端(比如微信、微博、github、gitee)对外提供的对接方式都是授权码模式、OpenID Connect模式,最终获取到一段用户信息(比如用户名、头像地址、邮箱),但是其实并没有办法拿着这段信息在当前系统中访问受限资源,以前都是手搓token或者其它手段来得到受限访问的权限,这种方法不可靠也不安全,而且也不易维护。
jeecg针对以上场景,基于Spring Authorization Server扩展了social模式,用于处理获取三方用户信息后,再获取当前系统的访问凭证。
social模式实现源码:package org.jeecg.config.security.social;
提示:文档中只讲解social模式的应用,不讲解从三方登录到应用social模式的全流程,jeecg前后端均已实现,细节请查看源码。
请求地址:{baseUrl} /oauth2/token
请求方法:POST
请求头:
请求头名称 请求头值 Authorization Basic base64(clientId:clientSecret)(此处需要自行替换) Content-Type application/x-www-form-urlencoded
请求参数:
参数名称 参数值 grant_type 固定为social token 可获取用户信息的凭证 thirdType 三方来源
响应内容:
参数名称 参数含义 access_token 访问token,在被限制访问的接口请求中添加Authorization: Bearer access_token refersh_token 刷新token,用于刷新码模式获取新的access_token userInfo 当前登录用户信息 ... 其它内容不作详解,请查看源码
权限校验
可用于方法或类上,将基于注解的权限code,针对性处理方法或当前类的所有接口进行权限拦截。
基于角色
// shiro用法
@RequiresRoles("admin")
// 可替换为 spring authorization server 用法
@PreAuthorize("jps.requiresRoles('admin')")
基于权限
// shiro用法
@RequiresPermissions("sys:role")
// 可替换为 spring authorization server 用法
@PreAuthorize("jps.requiresPermissions('sys:role')")
角色和权限组合使用
- @PreAuthorize("@jps.requiresPermissions('system:quartzJob:add') or @jps.requiresRoles('admin')")
免登录配置
jeecg:
shiro:
excludeUrls: /test/jeecgDemo/demo3,/test/jeecgDemo/redisDemo/**,/jmreport/bigscreen2/**
# 替换为
security:
oauth2:
client:
ignore-urls:
- /test/jeecgDemo/demo3
- /test/jeecgDemo/redisDemo/**
- /jmreport/bigscreen2/**
升级小技巧
搜索 替换为 org.apache.shiro.SecurityUtils org.jeecg.config.security.utils.SecureUtil (LoginUser) SecurityUtils.getSubject().getPrincipal() SecureUtil.currentUser() org.apache.shiro.authz.annotation.RequiresRoles org.springframework.security.access.prepost.PreAuthorize org.apache.shiro.authz.annotation.RequiresPermissions org.springframework.security.access.prepost.PreAuthorize @RequiresPermissions @PreAuthorize("jps.requiresPermissions('xxx')") @RequiresRoles @PreAuthorize("@jps.requiresRoles('xxx')")
升级SQL
切换springboot3_sas分支的Spring Authorization Server,需要执行升级sql
CREATE TABLE `oauth2_registered_client` (
`id` varchar(100) NOT NULL,
`client_id` varchar(100) NOT NULL,
`client_id_issued_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`client_secret` varchar(200) DEFAULT NULL,
`client_secret_expires_at` timestamp NULL DEFAULT NULL,
`client_name` varchar(200) NOT NULL,
`client_authentication_methods` varchar(1000) NOT NULL,
`authorization_grant_types` varchar(1000) NOT NULL,
`redirect_uris` varchar(1000) DEFAULT NULL,
`post_logout_redirect_uris` varchar(1000) DEFAULT NULL,
`scopes` varchar(1000) NOT NULL,
`client_settings` varchar(2000) NOT NULL,
`token_settings` varchar(2000) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
INSERT INTO `oauth2_registered_client`
(`id`,
`client_id`,
`client_id_issued_at`,
`client_secret`,
`client_secret_expires_at`,
`client_name`,
`client_authentication_methods`,
`authorization_grant_types`,
`redirect_uris`,
`post_logout_redirect_uris`,
`scopes`,
`client_settings`,
`token_settings`)
VALUES
('3eacac0e-0de9-4727-9a64-6bdd4be2ee1f',
'jeecg-client',
now(),
'secret',
null,
'3eacac0e-0de9-4727-9a64-6bdd4be2ee1f',
'client_secret_basic',
'refresh_token,authorization_code,password,app,phone,social',
'http://127.0.0.1:8080/jeecg-',
'http://127.0.0.1:8080/',
'*',
'{"@class":"java.util.Collections$UnmodifiableMap","settings.client.require-proof-key":false,"settings.client.require-authorization-consent":true}',
'{"@class":"java.util.Collections$UnmodifiableMap","settings.token.reuse-refresh-tokens":true,"settings.token.id-token-signature-algorithm":["org.springframework.security.oauth2.jose.jws.SignatureAlgorithm","RS256"],"settings.token.access-token-time-to-live":["java.time.Duration",300000.000000000],"settings.token.access-token-format":{"@class":"org.springframework.security.oauth2.server.authorization.settings.OAuth2TokenFormat","value":"self-contained"},"settings.token.refresh-token-time-to-live":["java.time.Duration",3600.000000000],"settings.token.authorization-code-time-to-live":["java.time.Duration",300000.000000000],"settings.token.device-code-time-to-live":["java.time.Duration",300000.000000000]}');
常用API
1. 获取登录用户信息
LoginUser sysUser = SecureUtil.currentUser();