跳到主要内容

升级SpringSAS权限

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

重要提示:当前是beta阶段,正式项目不建议使用

源码下载:

登录对接

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

请求头:

请求头名称请求头值
AuthorizationBasic base64(clientId:clientSecret)(此处需要自行替换)
Content-Typeapplication/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

请求头:

请求头名称请求头值
AuthorizationBasic base64(clientId:clientSecret)(此处需要自行替换)
Content-Typeapplication/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-Typeapplication/x-www-form-urlencoded

请求参数:

参数名称参数值
grant_type固定为social
token可获取用户信息的凭证
thirdType三方来源

响应内容:

参数名称参数含义
access_token访问token,在被限制访问的接口请求中添加Authorization: Bearer access_token
refersh_token刷新token,用于刷新码模式获取新的access_token
userInfo当前登录用户信息
...其它内容不作详解,请查看源码

自定义新登录规则

可以直接参考password目录或者phone目录去写, jeecg-boot-base-core/org/jeecg/config/security/password jeecg-boot-base-core/org/jeecg/config/security/phone 创建casConvert、casToken、casProvider,在provider里面就照着passwordProvider里面的抄,唯一不同的就是根据什么条件获取用户信息, 比如password,就是根据用户名找用户信息,再来匹配密码是否正确, phone就是根据手机号+验证号,然后ticket看里面有什么信息,如果只有用户名,也可以单纯用用户名来获取用户信息,然后后面操作就都是一样的了,直接搬过去。 不过这样的话,等于默认ticket是合法的,只要是从ticket获取到的用户名,我们就认为是合法的,这时我们就需要对ticket进行验伪操作,验证ticket是合法的

前端获取token接口

统一接口: '/oauth2/token',
参数:grant_type = 'password';
# 通过grant_type 区分不同登录规则

权限校验

可用于方法或类上,将基于注解的权限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();

2. Shiro与Spring Authorization Server(sas)的登录处理区别

登录模式Shiro
用户名密码请求路径:/sys/login
请求方法:POST
请求头:
Content-Type: application/json
请求参数:
{“username”:"", "password":"", captcha: "", checkKey: xxxxxx}
手机号验证码请求路径:/sys/phoneLogin
请求方法:POST
请求头:
Content-Type: application/json
请求参数:
{“mobile”:"", captcha: ""}
三方登录回调请求路径:/sys/thirdLogin/getLoginUser/{token}/{thirdType}/{tenantId}
请求方法:GET
请求头:无
请求参数:路径参数
移动端登录请求路径:/sys/mlogin
请求方法:POST
请求头:
Content-Type: application/json
请求参数:
{“username”:"", "password":""}
登录模式Spring Authorization Server
用户名密码请求路径:/oauth2/token
请求方法:POST
请求头:
Content-Type: application/x-www-form-urlencoded<br />Authorization: Basic base64(clientId:clientSecret)
请求参数:
username: ""<br />password:""<br />grant_type:"password"<br />captcha:""<br />checkKey:xxxxxxx
手机号验证码请求路径:/oauth2/token
请求方法:POST
请求头:
Content-Type: application/x-www-form-urlencoded<br />Authorization: Basic base64(clientId:clientSecret)
请求参数:
mobile: ""<br />captcha:""<br />grant_type:"phone"
三方登录回调请求路径:/oauth2/token
请求方法:POST
请求头:
Content-Type: application/x-www-form-urlencoded<br />Authorization: Basic base64(clientId:clientSecret)
请求参数:
token: ""<br />thirdType:""(可选)<br />grant_type:"social"
移动端登录请求路径:/oauth2/token
请求方法:POST
请求头:
Content-Type: application/x-www-form-urlencoded<br />Authorization: Basic base64(clientId:clientSecret)
请求参数:
username: ""<br />password:""<br />grant_type:"app"

Apache Shiro:

  1. Shiro 是一个全面的安全框架,设计用于处理身份验证(Authentication)、授权(Authorization)、会话管理(Session Management)和加密等功能。它适用于各种类型的应用程序,包括Web、命令行、移动和Swing应用程序,不仅限于Web环境。
  2. Shiro 是轻量级的,不强制依赖任何特定的框架或容器,可以独立运行。这使得它在非Spring生态系统中也十分有用。
  3. Shiro 提供了易于理解的API,使得开发者能够快速实现安全功能。它的配置和使用通常被认为更为简单直接,对于小型到中型项目尤其友好。
  4. 尽管提供了广泛的安全功能,Shiro 在高级安全特性,如OAuth支持上可能不如Spring Security丰富,可能需要开发者自行实现或集成其他库。

Spring Authorization Server:

  1. Spring Authorization Server 是一个专注于实现OAuth 2.1授权服务器功能的框架,它是Spring Security项目的一部分,专门用于构建符合OAuth 2.1规范的认证服务器。这意味着它的主要关注点在于处理授权、令牌管理和OAuth相关的安全协议。
  2. 作为Spring家族的一员,Spring Authorization Server 与Spring Security和Spring Boot有着天然的紧密集成,对于使用Spring技术栈的项目来说,集成和配置更为便捷,能够利用Spring的强大功能和现有基础设施。
  3. Spring Authorization Server 紧密遵循OAuth 2.1 Authorization Framework和其他相关规范,确保了实现的合规性和安全性。
  4. 相比Shiro的广泛适用性,Spring Authorization Server提供了更专业的OAuth解决方案,包括支持OpenID Connect、JWT、JOSE(Javascript Object Signing and Encryption)等高级安全特性,适合需要实现OAuth服务的复杂应用场景。

如果你需要一个全面的安全框架处理应用程序的认证、授权等基本安全需求,Shiro可能是一个不错的选择,特别是对于那些不依赖Spring或者需要轻量级解决方案的项目。

如果你的应用需要实现OAuth 2.1授权服务器功能,那么Spring Authorization Server将是更专业的选择,它能提供更深入的OAuth支持和更好的Spring生态集成体验。