Spring Security 實戰乾貨:基於註解的介面角色訪問控制
1. 前言
歡迎閱讀 Spring Security 實戰乾貨[1] 系列文章 。在上一篇 基於配置的介面角色訪問控制[2] 我們講解了如何通過 javaConfig 的方式配置介面的角色訪問控制。其實還有一種更加靈活的配置方式 基於註解 。今天我們就來探討一下。DEMO 獲取方式在文末。
2. Spring Security 方法安全
Spring Security 基於註解的安全認證是通過在相關的方法上進行安全註解標記來實現的。
2.1 開啟全域性方法安全
我們可以在任何 @Configuration
例項上使用 @EnableGlobalMethodSecurity
註解來啟用全域性方法安全註解功能。該註解提供了三種不同的機制來實現同一種功能,所以我們單獨開一章進行探討。
3. @EnableGlobalMethodSecurity 註解
@Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME)
@Target(value = { java.lang.annotation.ElementType.TYPE })
@Documented
@Import({ GlobalMethodSecuritySelector.class })
@EnableGlobalAuthentication
@Configuration
public @interface EnableGlobalMethodSecurity {
/**
* 基於表示式進行方法訪問控制
*/
boolean prePostEnabled() default false;
/**
* 基於 @Secured 註解
*/
boolean securedEnabled() default false;
/**
* 基於 JSR-250 註解
*/
boolean jsr250Enabled() default false;
boolean proxyTargetClass() default false;
int order() default Ordered.LOWEST_PRECEDENCE;
}
@EnableGlobalMethodSecurity
原始碼中提供了 prePostEnabled
、securedEnabled
和 jsr250Enabled
三種方式。當你開啟全域性基於註解的方法安全功能時,也就是使用 @EnableGlobalMethodSecurity
註解時我們需要選擇使用這三種的一種或者其中幾種。我們接下來將分別介紹它們。
4. 使用 prePostEnabled
如果你在 @EnableGlobalMethodSecurity
設定 prePostEnabled
為 true
,則開啟了基於表示式的方法安全控制。通過表示式運算結果的布林值來決定是否可以訪問(true
開放, false
拒絕 )。有時您可能需要執行開啟 prePostEnabled
複雜的操作。對於這些例項,您可以擴充套件 GlobalMethodSecurityConfiguration
,確保子類上存在@EnableGlobalMethodSecurity(prePostEnabled = true)
。例如,如果要提供自定義 MethodSecurityExpressionHandler
:
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {
@Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
// ... create and return custom MethodSecurityExpressionHandler ...
return expressionHandler;
}
}
上面示例屬於高階操作,一般沒有必要。無論是否繼承GlobalMethodSecurityConfiguration
都將會開啟四個註解。@PreAuthorize
和 @PostAuthorize
側重於方法呼叫的控制;而 @PreFilter
和 @PostFilter
側重於資料的控制。
4.1 @PreAuthorize
在標記的方法呼叫之前,通過表示式來計算是否可以授權訪問。接下來我來總結以下常用的表示式。
- 基於
SecurityExpressionOperations
介面的表示式,也就是我們在上一文[3]的javaConfig
配置。示例:@PreAuthorize("hasRole('ADMIN')")
必須擁有ROLE_ADMIN
角色。 - 基於
UserDetails
的表示式,此表示式用以對當前使用者的一些額外的限定操作。示例:@PreAuthorize("principal.username.startsWith('Felordcn')")
使用者名稱開頭為Felordcn
的使用者才能訪問。 - 基於對入參的
SpEL
表示式處理。關於SpEL
表示式可參考官方文件。或者通過關注公眾號:Felordcn 來獲取相關資料。示例:@PreAuthorize("#id.equals(principal.username)")
入參id
必須同當前的使用者名稱相同。
4.2 @PostAuthorize
在標記的方法呼叫之後,通過表示式來計算是否可以授權訪問。該註解是針對 @PreAuthorize
。區別在於先執行方法。而後進行表示式判斷。如果方法沒有返回值實際上等於開放許可權控制;如果有返回值實際的結果是使用者操作成功但是得不到響應。
4.3 @PreFilter
基於方法入參相關的表示式,對入參進行過濾。分頁慎用!該過程發生在介面接收引數之前。 入參必須為 java.util.Collection
且支援 remove(Object)
的引數。如果有多個集合需要通過 filterTarget=<引數名>
來指定過濾的集合。內建保留名稱 filterObject
作為集合元素的操作名來進行評估過濾。
樣例:
// 入參為Collection<String> ids 測試資料 ["Felordcn","felord","jetty"]
// 過濾掉 felord jetty 為 Felordcn
@PreFilter(value = "filterObject.startsWith('F')",filterTarget = "ids")
// 如果 當前使用者持有 ROLE_AD 角色 引數都符合 否則 過濾掉不是 f 開頭的
// DEMO 使用者不持有 ROLE_AD 角色 故而 集合只剩下 felord
@PreFilter("hasRole('AD') or filterObject.startsWith('f')")
4.4 @PostFilter
和@PreFilter
不同的是, 基於返回值相關的表示式,對返回值進行過濾。分頁慎用!該過程發生介面進行資料返回之前。 相關測試與 @PreFilter
相似,參見文末提供的 DEMO。
5. 使用 securedEnabled
如果你在 @EnableGlobalMethodSecurity
設定 securedEnabled
為 true
,就開啟了角色註解 @Secured
,該註解功能要簡單的多,預設情況下只能基於角色(預設需要帶字首 ROLE_
)集合來進行訪問控制決策。
該註解的機制是隻要其宣告的角色集合(value
)中包含當前使用者持有的任一角色就可以訪問。也就是 使用者的角色集合和 @Secured
註解的角色集合要存在非空的交集。不支援使用 SpEL 表示式進行決策。
6. 使用 jsr250Enabled
啟用 JSR-250 安全控制註解,這屬於 JavaEE 的安全規範(現為 jakarta 專案)。一共有五個安全註解。如果你在 @EnableGlobalMethodSecurity
設定 jsr250Enabled
為 true
,就開啟了 JavaEE 安全註解中的以下三個:
- @DenyAll 拒絕所有的訪問
- @PermitAll 同意所有的訪問
- @RolesAllowed 用法和 5. 中的
@Secured
一樣。
7. 總結
今天講解了 Spring Security 另一種基於註解的靜態配置。相比較基於 javaConfig
的方式要靈活一些、粒度更細、基於 SpEL 表示式可以實現更加強大的功能。但是這兩種的方式還是基於程式設計的靜態方式,具有一定的侷限性。更加靈活的方式應該是動態來處理使用者的角色和資源的對映關係,這是以後我們將要解決的問題。本次的 DEMO 可通過 關注微信公眾號:Felordcn 回覆 ss09 獲取。
參考資料
Spring Security 實戰乾貨: https://www.felord.cn/categories/spring-security/
[2]基於配置的介面角色訪問控制: https://www.felord.cn/spring-security-javaconfig-rbac.html
[3]上一文: https://www.felord.cn/spring-security-javaconfig-rbac.html
相關文章
- Spring Security 實戰乾貨:基於配置的介面角色訪問控制Spring
- Spring Security 實戰乾貨:使用 JWT 認證訪問介面SpringJWT
- Spring Security 實戰乾貨:理解AuthenticationManagerSpring
- .Net Core實戰之基於角色的訪問控制的設計
- Spring Security 實戰乾貨:如何實現不同的介面不同的安全策略Spring
- Spring Security 實戰乾貨:圖解Spring Security中的Servlet過濾器體系Spring圖解Servlet過濾器
- 基於角色管理的系統訪問控制
- Spring Security 實戰乾貨:分散式物件SharedObjectSpring分散式物件Object
- 服務端指南 | 基於角色的訪問控制服務端
- Spring Security實現基於RBAC的許可權表示式動態訪問控制Spring
- Spring Security 實戰乾貨:AuthenticationManager的初始化細節Spring
- Spring Security 實戰乾貨:實現自定義退出登入Spring
- Spring Security 實戰乾貨:玩轉自定義登入Spring
- 基於角色的訪問控制RBAC是什麼? - TailscaleAI
- 資料安全合規需要從基於角色的訪問控制邁向基於屬性的訪問控制
- Quarkus中基於角色的許可權訪問控制教程
- 【解構雲原生】K8s 的 RBAC - 基於角色的訪問控制K8S
- spring基於註解配置實現事務控制Spring
- Ant Design Pro v4 基於角色的許可權訪問控制實戰教程 #1 介紹
- Spring Security 實戰乾貨:客戶端OAuth2授權請求的Spring客戶端OAuth
- Spring Security 實戰乾貨:OAuth2登入獲取Token的核心邏輯SpringOAuth
- Spring Security 實戰乾貨:客戶端OAuth2授權請求的入口Spring客戶端OAuth
- Spring Aop基於註解的實現Spring
- Spring Security 實戰乾貨:從零手寫一個驗證碼登入Spring
- Mybatis基於註解的方式訪問資料庫MyBatis資料庫
- Ant Design Pro v4 基於角色的許可權訪問控制實戰教程 #2 初始化專案
- Spring Security原始碼分析十三:Spring Security 基於表示式的許可權控制Spring原始碼
- Spring Boot 最核心的 25 個註解,都是乾貨!Spring Boot
- Spring Security小教程 Vol 6. 初識訪問控制Spring
- Spring Security 實戰乾貨:OAuth2授權請求是如何構建並執行的SpringOAuth
- Spring Security 實戰乾貨:OAuth2第三方授權初體驗SpringOAuth
- 乾貨!基於SpringBoot的RabbitMQ多種模式佇列實戰Spring BootMQ模式佇列
- 基於註解的 Spring MVC詳解SpringMVC
- 基於PLM訪問控制模型研究模型
- Spring基於註解的IoC配置Spring
- Spring基於註解的aop配置Spring
- Spring(5、基於註解的事物)Spring
- 你們要的乾貨來了——實戰 Spring BootSpring Boot