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();