Spring Security實戰(zhàn)干貨:基于注解的接口角色訪問控制
1. 前言
歡迎閱讀 Spring Security 實戰(zhàn)干貨 系列文章 。在上一篇 基于配置的接口角色訪問控制 我們講解了如何通過 javaConfig 的方式配置接口的角色訪問控制。其實還有一種更加靈活的配置方式 基于注解 。今天我們就來探討一下。DEMO 獲取方式在文末。
2. Spring Security 方法安全
Spring Security 基于注解的安全認(rèn)證是通過在相關(guān)的方法上進(jìn)行安全注解標(biāo)記來實現(xiàn)的。
2.1 開啟全局方法安全
我們可以在任何 @Configuration實例上使用 @EnableGlobalMethodSecurity 注解來啟用全局方法安全注解功能。該注解提供了三種不同的機(jī)制來實現(xiàn)同一種功能,所以我們單獨開一章進(jìn)行探討。
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 {
- /**
- * 基于表達(dá)式進(jìn)行方法訪問控制
- */
- 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 三種方式。當(dāng)你開啟全局基于注解的方法安全功能時,也就是使用 @EnableGlobalMethodSecurity 注解時我們需要選擇使用這三種的一種或者其中幾種。我們接下來將分別介紹它們。
4. 使用 prePostEnabled
如果你在 @EnableGlobalMethodSecurity 設(shè)置 prePostEnabled 為 true ,則開啟了基于表達(dá)式的方法安全控制。通過表達(dá)式運算結(jié)果的布爾值來決定是否可以訪問(true 開放, false 拒絕 )。
有時您可能需要執(zhí)行開啟 prePostEnabled 復(fù)雜的操作。對于這些實例,您可以擴(kuò)展 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 側(cè)重于方法調(diào)用的控制;而 @PreFilter 和 @PostFilter 側(cè)重于數(shù)據(jù)的控制。
4.1 @PreAuthorize
在標(biāo)記的方法調(diào)用之前,通過表達(dá)式來計算是否可以授權(quán)訪問。接下來我來總結(jié)以下常用的表達(dá)式。
- 基于 SecurityExpressionOperations 接口的表達(dá)式,也就是我們在上一文的 javaConfig 配置。示例: @PreAuthorize("hasRole('ADMIN')") 必須擁有 ROLE_ADMIN 角色。
- 基于 UserDetails 的表達(dá)式,此表達(dá)式用以對當(dāng)前用戶的一些額外的限定操作。示例:@PreAuthorize("principal.username.startsWith('Felordcn')") 用戶名開頭為 Felordcn 的用戶才能訪問。
- 基于對入?yún)⒌?SpEL表達(dá)式處理。關(guān)于 SpEL 表達(dá)式可參考官方文檔。或者通過關(guān)注公眾號:Felordcn 來獲取相關(guān)資料。 示例: @PreAuthorize("#id.equals(principal.username)") 入?yún)?id 必須同當(dāng)前的用戶名相同。
4.2 @PostAuthorize
在標(biāo)記的方法調(diào)用之后,通過表達(dá)式來計算是否可以授權(quán)訪問。該注解是針對 @PreAuthorize 。區(qū)別在于先執(zhí)行方法。而后進(jìn)行表達(dá)式判斷。如果方法沒有返回值實際上等于開放權(quán)限控制;如果有返回值實際的結(jié)果是用戶操作成功但是得不到響應(yīng)。
4.3 @PreFilter
基于方法入?yún)⑾嚓P(guān)的表達(dá)式,對入?yún)⑦M(jìn)行過濾。分頁慎用!該過程發(fā)生在接口接收參數(shù)之前。 入?yún)⒈仨殲?java.util.Collection 且支持 remove(Object) 的參數(shù)。如果有多個集合需要通過 filterTarget=<參數(shù)名> 來指定過濾的集合。內(nèi)置保留名稱 filterObject 作為集合元素的操作名來進(jìn)行評估過濾。
樣例:
- // 入?yún)镃ollection<String> ids 測試數(shù)據(jù) ["Felordcn","felord","jetty"]
- // 過濾掉 felord jetty 為 Felordcn
- @PreFilter(value = "filterObject.startsWith('F')",filterTarget = "ids")
- // 如果 當(dāng)前用戶持有 ROLE_AD 角色 參數(shù)都符合 否則 過濾掉不是 f 開頭的
- // DEMO 用戶不持有 ROLE_AD 角色 故而 集合只剩下 felord
- @PreFilter("hasRole('AD') or filterObject.startsWith('f')")
4.4 @PostFilter
和@PreFilter 不同的是, 基于返回值相關(guān)的表達(dá)式,對返回值進(jìn)行過濾。分頁慎用!該過程發(fā)生接口進(jìn)行數(shù)據(jù)返回之前。 相關(guān)測試與 @PreFilter 相似,參見文末提供的 DEMO。
5. 使用 securedEnabled
如果你在 @EnableGlobalMethodSecurity 設(shè)置 securedEnabled 為 true ,就開啟了角色注解 @Secured ,該注解功能要簡單的多,默認(rèn)情況下只能基于角色(默認(rèn)需要帶前綴 ROLE_)集合來進(jìn)行訪問控制決策。
該注解的機(jī)制是只要其聲明的角色集合(value)中包含當(dāng)前用戶持有的任一角色就可以訪問。也就是 用戶的角色集合和 @Secured 注解的角色集合要存在非空的交集。 不支持使用 SpEL 表達(dá)式進(jìn)行決策。
6. 使用 jsr250Enabled
啟用 JSR-250 安全控制注解,這屬于 JavaEE 的安全規(guī)范(現(xiàn)為 jakarta 項目)。一共有五個安全注解。如果你在 @EnableGlobalMethodSecurity 設(shè)置 jsr250Enabled 為 true ,就開啟了 JavaEE 安全注解中的以下三個:
- @DenyAll 拒絕所有的訪問
- @PermitAll 同意所有的訪問
- @RolesAllowed 用法和 5. 中的 @Secured 一樣。
7. 總結(jié)
今天講解了 Spring Security 另一種基于注解的靜態(tài)配置。相比較基于 javaConfig 的方式要靈活一些、粒度更細(xì)、基于 SpEL 表達(dá)式可以實現(xiàn)更加強(qiáng)大的功能。但是這兩種的方式還是基于編程的靜態(tài)方式,具有一定的局限性。更加靈活的方式應(yīng)該是動態(tài)來處理用戶的角色和資源的映射關(guān)系,這是以后我們將要解決的問題。
本次的 DEMO 可通過 關(guān)注微信公眾號:Felordcn 回復(fù) ss09 獲取。