Spring Security 實戰乾貨:基於註解的接口角色訪問控制

1. 前言

歡迎閱讀 Spring Security 實戰乾貨 系列文章 。在上一篇 基於配置的接口角色訪問控制 咱們講解了如何經過 javaConfig 的方式配置接口的角色訪問控制。其實還有一種更加靈活的配置方式 基於註解 。今天咱們就來探討一下。DEMO 獲取方式在文末。html

2. Spring Security 方法安全

Spring Security 基於註解的安全認證是經過在相關的方法上進行安全註解標記來實現的。java

2.1 開啓全局方法安全

咱們能夠在任何 @Configuration實例上使用 @EnableGlobalMethodSecurity 註解來啓用全局方法安全註解功能。該註解提供了三種不一樣的機制來實現同一種功能,因此咱們單獨開一章進行探討。spring

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 源碼中提供了 prePostEnabledsecuredEnabledjsr250Enabled 三種方式。當你開啓全局基於註解的方法安全功能時,也就是使用 @EnableGlobalMethodSecurity 註解時咱們須要選擇使用這三種的一種或者其中幾種。咱們接下來將分別介紹它們。express

4. 使用 prePostEnabled

若是你在 @EnableGlobalMethodSecurity 設置 prePostEnabledtrue ,則開啓了基於表達式的方法安全控制。經過表達式運算結果的布爾值來決定是否能夠訪問(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 側重於數據的控制。segmentfault

4.1 @PreAuthorize

在標記的方法調用以前,經過表達式來計算是否能夠受權訪問。接下來我來總結如下經常使用的表達式。安全

  • 基於 SecurityExpressionOperations 接口的表達式,也就是咱們在上一文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 做爲集合元素的操做名來進行評估過濾。 ide

樣例:測試

// 入參爲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 設置 securedEnabledtrue ,就開啓了角色註解 @Secured ,該註解功能要簡單的多,默認狀況下只能基於角色(默認須要帶前綴 ROLE_)集合來進行訪問控制決策。

該註解的機制是隻要其聲明的角色集合(value)中包含當前用戶持有的任一角色就能夠訪問。也就是 用戶的角色集合和 @Secured 註解的角色集合要存在非空的交集。 不支持使用 SpEL 表達式進行決策。

6. 使用 jsr250Enabled

啓用 JSR-250 安全控制註解,這屬於 JavaEE 的安全規範(現爲 jakarta 項目)。一共有五個安全註解。若是你在 @EnableGlobalMethodSecurity 設置 jsr250Enabledtrue ,就開啓了 JavaEE 安全註解中的如下三個:

  • @DenyAll 拒絕全部的訪問
  • @PermitAll 贊成全部的訪問
  • @RolesAllowed 用法和 5. 中的 @Secured 同樣。

7. 總結

今天講解了 Spring Security 另外一種基於註解的靜態配置。相比較基於 javaConfig 的方式要靈活一些、粒度更細、基於 SpEL 表達式能夠實現更增強大的功能。可是這兩種的方式仍是基於編程的靜態方式,具備必定的侷限性。更加靈活的方式應該是動態來處理用戶的角色和資源的映射關係,這是之後咱們將要解決的問題。
本次的 DEMO 可經過 關注微信公衆號:Felordcn 回覆 ss09 獲取。

關注公衆號:Felordcn 獲取更多資訊

我的博客:https://felord.cn

相關文章
相關標籤/搜索