Spring Security 整合 RBAC
探討如何利用 Spring Security 提供的自訂方式整合 RBAC

目前比較主流的 Role-based Access Control, RBAC 算是比較成熟且彈性的權限架構,若要在 Spring Security 實作 RBAC 並搭配 JWT 或 OAuth 2.0,大概要探討兩個問題

  1. 是否值得為了減少查詢資料源的次數,而將角色或權限直接置入 JWT 或 Authorization Server 回傳的 token
  2. 是否採取 Spring Security 原生只使用最細粒度的權限來進行存取控制

關於第一個問題,目前的思考點大概就是考量系統的併發度跟使用者總數

如果需要高併發,那自然希望盡量減少查詢資料的次數或資料量,盡量避免同時大量消耗 DB 資源;相反的,如果不需要高併發,那每次驗證都重新取得角色及權限資料也沒關係

使用者數量的考量也是類似,如果每次查詢的成本太高,則盡量減少查詢的次數

而第二個問題,則是要考量實際的業務場景

如果角色跟權限很大量且複雜,或可能要考慮角色分層甚至使用者群組,或不同 Domain 有不同的權限邏輯,則可能用 Spring Security 原生方式會不敷使用,相對如果角色權限結構簡單,則直接使用原生方式可能成本較低

所以以下探討主要專注於使用 Spring Security 提供的自訂空間來實作較為複雜的存取控制

Spring Security 權限架構

首先要探討 Spring Security 原生提供的權限架構

  • GrantedAuthority: 可以將每個 GrantedAuthority 想成是個別的 privilege,所以在 Spring Security 中,authority 就是 privilege 的概念
  • Role: 在 Spring Security 中為了保持彈性,Role 可以當作 Authority 也可以當作一組 Authority 的組合
    • Role as Authority: 直接把 Role 當成一種 Authority 的情況,就是將 Role 當作一個有 ROLE_ 前綴的字串,這種情況下 Role 跟 Authority 就只是語意上的差別
    • Role as Container: Role 可以包含一組 Authority,Spring Security 在這個概念上沒有提供太多指引說明,需要自行實作

Granted Authority Versus Role in Spring Security

所以概念上 Spring Security 的 Role 比較像是有特別命名方式 (ROLE_ 前綴) 的一種 Authority,因此在預設中,hasAuthority('ROLE_ADMIN')hasRole('ADMIN') 是一樣的

在 Spring Security 中 Role as Authority 最明顯的例子就是 SimpleGrantedAuthority,建構子直接吃 role 字串,getAuthority() 方法也直接吐回 role 字串屬性

因此,如果要建立 Role 跟 Authority 分離的權限架構,首先要確認的是在設定 access control 時,hasRole()hasAuthority() 兩個方法到底是用什麼機制、從哪裡取得

Spring Security 的整體架構分成驗證跟存取控制兩個部分,由於驗證部分會由 API 搭配 JWT 做掉大部分,甚至未來整合 OAuth 2.0,所以需要探討的只有如何在驗證過程將使用者資料及權限放入 SecurityContext 中讓後續的存取控制使用,並且存取控制部分如何依照使用者資料及權限進行控制

UserDetailsService

要在驗證過程將使用者資料及權限放入 SecurityContext 中,最簡單就是實作 UserDetailsServiceloadUserByUsername 方法,然後將自訂的 UserDetailsService 提供給 AuthenticationManager 即可

這部分主要還是要考慮繼承 UserDetails 物件後,如何設定權限部分以及該將什麼樣的資料放入 Authentication 的 authorities 中

AccessDecisionVoterAccessDecisionManager

而存取控制的邏輯部分就要實作 AccessDecisionVotervote 方法

voter 可以選擇回傳三種決策:

  • ACCESS_GRANTED: 通過
  • ACCESS_DENIED: 否決
  • ACCESS_ABSTAIN: 棄權

實作完 voter 後,要將 voter 加入 AccessDecisionManager,而 AccessDecisionManager 預設有三種決策策略

  • AffirmativeBased: 任何 voter 回傳通過即可
  • ConsensusBased: 通過數大於否決數,忽略棄權
  • UnanimousBased: 所有 voter 通過或棄權,也就是不能有任何否決

Custom AccessDecisionVoters in Spring Security

這部分就只能基於實際業務邏輯做考量


Last modified on 2019-05-30