1. 引言
Spring Security 是 Spring Framework 的擴展,旨在簡化我們在應用程序中實施常見安全實踐的過程。這包括用户身份驗證和授權、API 保護以及更多功能。
在本教程中,我們將探討 Spring Security 中眾多組件之一:<em >AuthorizationManager</em>。我們將瞭解它如何融入更大的 Spring Security 生態系統,以及它如何幫助我們安全地保護應用程序的各種用例。
2. Spring Security 中的 AuthorizationManager
Spring Security 中的 AuthorizationManager 是一個接口,允許我們檢查已認證的實體是否可以訪問受保護的資源。 AuthorizationManager 實例用於 Spring Security 以做出基於請求、基於方法和基於消息的組件的最終訪問控制決策。
背景信息:Spring Security 有幾個關鍵概念,在瞭解 AuthorizationManager 的特定作用之前,有幫助。
- 實體:任何可以向系統發出請求的實體。 這可以是人類用户或遠程 Web 服務,例如。
- 身份驗證:驗證實體是否是他們聲稱的身份的過程。 這可以通過用户名/密碼、令牌或其他多種方法進行。
- 授權:驗證實體是否可以訪問資源的過程
- 資源:系統可供訪問的信息——例如,URL 或文檔
- 權限:通常稱為角色,它代表實體擁有的權限。 單個實體可能被授予零個或多個權限。
在瞭解這些概念之後,我們可以更深入地研究 AuthorizationManager 接口。
2.1. 如何使用 AuthorizationManager
AuthorizationManager 是一個簡單的接口,僅包含兩個方法:
AuthorizationDecision check(Supplier<Authentication> authentication, T object);
void verify(Supplier<Authentication> authentication, T object);
兩種方法看起來很相似,因為它們接受相同的參數:
- 身份驗證:一個 供應商,它提供一個 身份驗證對象,代表發起請求的實體。
- 對象:所請求的安全對象(將取決於請求的性質而異)
然而,每種方法都有不同的用途。第一種方法返回一個 授權決策對象,它是一個簡單的包裝器,封裝了一個 布爾值,指示實體是否可以訪問安全對象。
第二種方法不返回任何內容。 相反,它只是執行授權檢查,並在實體未被授權訪問安全對象時拋出 AccessDeniedException。
2.2. 舊版本的 Spring Security
需要注意的是,<em >AuthorizationManager</em> 接口是在 Spring Security 5.0 中引入的。在此接口之前,授權的主要方式是通過 <em >AccessDecisionManager</em> 接口。儘管 <em >AccessDecisionManager</em> 接口在 Spring Security 的最新版本中仍然存在,但它已被棄用,應該避免使用,而應使用 <strong >AuthorizationManager</strong>。
3. AuthorizationManager 的實現
Spring 提供了多個 AuthorizationManager 接口的實現。在後面的章節中,我們將詳細介紹其中一些。
3.1. AuthenticatedAuthorizationManager
The first implementation we’ll look at is the AuthenticatedAuthorizationManager. Put simply, this class returns a positive authorization decision based solely on whether or not the entity is authenticated. Additionally, it supports three levels of authentication:
- anonymous: the entity is not authenticated
- remember me: the entity is authenticated and is using remembered credentials
- fully authenticated: the entity is authenticated and not using remembered credentials
Note that this is the default AuthorizationManager that Spring Boot creates for web-based applications. By default, all endpoints will allow access regardless of role or authority, as long as it comes from an authenticated entity.
3.2. AuthoritiesAuthorizationManager
此實現類似於之前的版本,但它能夠基於多個權威進行決策。 這更適合於複雜的應用程序,其中資源可能需要由多個權威訪問。
考慮一個博客系統,它使用不同的角色來管理髮布過程。 創建和保存文章的資源可能對 作者 和 編輯 角色都可訪問,但發佈資源的權限僅限於 編輯 角色。
3.3. AuthorityAuthorizationManager
這個實現相當直接。 它根據實體是否具有特定角色來做出所有授權決策。
這個實現適用於簡單的應用程序,其中每個資源只需要一個角色或權限。例如,它非常適合僅向具有 Administrator 角色的實體保護一組特定的 URL。
請注意,這個實現將決策權委託給 AuthoritiesAuthorizationManager 的實例。 此外,它也是 Spring 使用 hasRole() 或 hasAuthorities() 時使用的實現,尤其是在自定義 SecurityFilterChain 時。
3.4. <em>RequestMatcherDelegatingAuthorizationManager</em>
此實現實際上不進行授權決策。相反,它根據 URL 模式委託給另一個實現,通常是上述的管理器類之一。
例如,如果有一些 URL 是公開的並且可以供任何人訪問,我們可以將這些 URL 委託給一個空操作(no-op)實現,該實現始終返回積極的授權結果。然後,我們可以將受保護的請求委託給一個 <em>AuthoritiesAuthorizationManager</em>,該實現負責檢查角色。
事實上,這正是 Spring 在我們向 <em>SecurityFilterChain</em> 添加新的請求匹配器時所做的。每次我們配置一個新的請求匹配器並指定一個或多個所需的角色或權限時,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 如何處理授權。我們瞭解了通用的 <em >AuthorizationManager</em> 接口及其兩個方法如何做出授權決策。
我們還看到了該接口的各種實現,以及它們在 Spring Security 框架中的使用方式。
最後,我們創建了一個簡單的自定義實現,可用於在我們的應用程序中做出任何所需的授權決策。