Spring Security 授權管理器
一、簡介
Spring Security 是 Spring 框架的擴展,可以輕鬆地將常見的安全實踐建置到我們的應用程式中。這包括用戶身份驗證和授權、API 保護等等。
在本教程中,我們將了解 Spring Security 內部的眾多部分之一: AuthorizationManager
。我們將了解它如何融入更大的 Spring Security 生態系統,以及它如何幫助保護我們的應用程式的各種用例。
2.什麼是Spring Security AuthorizationManager
Spring AuthorizationManager
是一個接口,允許我們檢查經過驗證的實體是否有權存取安全資源。 Spring Security 使用AuthorizationManager
實例為基於請求、基於方法和基於訊息的元件做出最終存取控制決策。
作為背景,Spring Security 有幾個關鍵概念,在了解AuthorizationManager
的具體角色之前有助於理解這些概念:
- 實體:任何可以向系統發出請求的東西。例如,這可能是人類使用者或遠端 Web 服務。
- 身份驗證:驗證實體是否是其自稱身份的過程。這可以透過使用者名稱/密碼、令牌或任意數量的其他方法來實現。
- 授權:驗證實體是否有權存取資源的過程
- 資源:系統可供存取的任何信息,例如 URL 或文檔
- 權限:通常稱為角色,這是表示實體擁有的權限的邏輯名稱。單一實體可能被授予零個或多個權限。
考慮到這些概念,我們可以更深入地研究AuthorizationManager
介面。
2.1.如何使用AuthorizationManager
AuthorizationManager
是一個簡單的接口,只包含兩個方法:
AuthorizationDecision check(Supplier<Authentication> authentication, T object);
void verify(Supplier<Authentication> authentication, T object);
這兩種方法看起來很相似,因為它們採用相同的參數:
-
authentication
:提供代表發出請求的實體Authentication
物件的Supplier
。 -
object
:被要求的安全物件(根據請求的性質而改變)
然而,每種方法都有不同的目的。第一個方法傳回一個AuthorizationDecision
,它是一個boolean
值的簡單包裝,指示實體是否可以存取安全物件。
第二種方法不回傳任何內容。相反,它只是執行授權檢查,如果實體無權存取安全對象,則拋出AccessDeniedException
。
2.2. Spring Security 的舊版本
值得注意的是, AuthorizationManager
介面是在Spring Security 5.0中引入的。在此介面之前,授權的主要方法是透過AccessDecisionManager
介面。雖然AccessDecisionManager
介面在 Spring Security 的最新版本中仍然存在,但它已被棄用,並且應該避免使用**AuthorizationManager** .
AuthorizationManager
的實現
Spring 提供了多種AuthorizationManager
介面的實作。在下面的部分中,我們將了解其中的幾個。
3.1. AuthenticatedAuthorizationManager
我們要查看的第一個實作是AuthenticatedAuthorizationManager
。簡而言之,此類僅根據實體是否經過身份驗證返回肯定的授權決策。此外,它還支援三個層級的身份驗證:
- 匿名:實體未經身份驗證
- 記住我:該實體已通過身份驗證並且正在使用記住的憑證
- 完全經過身份驗證:實體經過身份驗證並且不使用記住的憑證
請注意,這是 Spring Boot 為基於 Web 的應用程式建立的預設AuthorizationManager
。預設情況下,所有端點都將允許訪問,無論角色或權限如何,只要它來自經過身份驗證的實體即可。
3.2. AuthoritiesAuthorizationManager
此實作的工作原理與前一個類似,只是它可以根據多個權限做出決策。這更適合複雜的應用程序,其中資源可能需要由多個權限存取。
考慮一個使用不同角色來管理發布過程的部落格系統。 Author
和Editor
角色都可以存取用於建立和保存文章的資源。但是,用於發布的資源僅可供Editor
角色使用。
3.3. AuthorityAuthorizationManager
這個實作相當簡單。它根據實體是否具有特定角色來做出所有授權決策。
此實作非常適合每個資源需要單一角色或權限的簡單應用程式。例如,它可以很好地保護一組特定的 URL,僅允許具有Administrator
角色的實體存取。
請注意,此實作將其決策委託給AuthoritiesAuthorizationManager
的實例。這也是 Spring 在自訂SecurityFilterChain
時呼叫hasRole()
或hasAuthorities()
時所使用的實作。
3.4. RequestMatcherDelegatingAuthorizationManager
該實作實際上並不做出授權決策。相反,它委託給另一個基於 URL 模式的實現,通常是上述管理器類別之一。
例如,如果我們有一些公開且可供任何人使用的 URL,我們可以將這些 URL 委託給始終傳回肯定授權的無操作實作。然後,我們可以將安全性請求委託給處理角色檢查的AuthoritiesAuthorizationManager
。
事實上,這正是 Spring 在我們為SecurityFilterChain
添加新的請求匹配器時所做的事情。每次我們配置新的請求匹配器並指定一個或多個所需的角色或權限時,Spring 都會建立此類的一個新實例以及適當的委託。
3.5. ObservationAuthorizationManager
我們將看到的最終實作是ObservationAuthorizationManager
。這個類別實際上只是另一個實現的包裝,並且具有記錄與授權決策相關的指標的附加功能。只要應用程式中存在有效的ObservationRegistry
,Spring 就會自動使用此實作。
3.6.其他實現
值得一提的是,Spring Security 中還存在其他幾個實作。其中大多數與用於保護方法的各種 Spring Security 註解相關:
-
SecuredAuthorizationManager -> @Secured
-
PreAuthorizeAuthorizationManager -> @PreAuthorize
-
PostAuthorizeAuthorizationManager -> @PostAuthorize
本質上,我們可以用來保護資源的任何 Spring Security 註解都有相應的AuthorityManager
實作。
3.7.使用多個AuthorizationManagers
在實務中,我們很少只使用AuthorizationManager
的單一實例。讓我們來看看SecurityFilterChain
bean 的範例:
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests((authorize) -> authorize
.requestMatchers("/posts/publish/**").hasRole("EDITOR")
.requestMatchers("/posts/create/**").hasAnyRole("EDITOR", "AUTHOR")
.anyRequest().permitAll());
return http.build();
}
此範例使用五個不同的AuthorizationManager
實例:
- 對
hasRole()
呼叫建立了一個AuthorityAuthorizationManager
實例,該實例又委託給一個新的AuthoritiesAuthorizationManager
實例。 - 對
hasAnyRole()
的呼叫也會建立一個AuthorityAuthorizationManager
實例,該實例又委託給一個新的AuthoritiesAuthorizationManager
實例。 - 對
permitAll()
的呼叫使用 Spring Security 提供的靜態無操作AuthorizationManager
,它始終提供積極的授權決策。
具有自己角色的附加請求匹配器以及任何基於方法的註解都會建立附加的AuthorizationManager
實例。
4.使用自訂AuthorizationManager
上面提供的實作足以滿足許多應用程式的需要。然而,與 Spring 中的許多介面一樣,完全可以建立一個自訂的AuthorizationManager
來滿足我們的任何需求。
讓我們定義一個自訂的AuthorizationManager
:
AuthorizationManager<RequestAuthorizationContext> customAuthManager() {
return new AuthorizationManager<RequestAuthorizationContext>() {
@Override
public AuthorizationDecision check(Supplier<Authentication> authentication, RequestAuthorizationContext object) {
// make authorization decision
}
};
}
然後,我們在自訂SecurityFilterChain
時傳遞此實例:
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests((authorize) ->
authorize.requestMatchers("/custom/**").access(customAuthManager())
return http.build();
}
在本例中,我們使用RequestAuthorizationContext
做出授權決策。此類提供對底層 HTTP 請求的訪問,這意味著我們可以根據 cookie、標頭等內容做出決策。我們也可以委託第三方服務、資料庫或快取等結構來做出我們想要的任何類型的授權決策。
5. 結論
在本文中,我們仔細研究了 Spring Security 如何處理授權。我們看到了通用的AuthorizationManager
介面以及它的兩個方法如何做出授權決策。
我們也看到了該實現的各種實現,以及它們如何在 Spring Security 框架的各個地方使用。
最後,我們創建了一個簡單的自訂實現,可用於在應用程式中做出我們需要的任何類型的授權決策。
與往常一樣,本文中的程式碼範例可以在 GitHub 上取得。