預先驗證情境
範例包括 X.509、Siteminder,以及應用程式執行所在的 Java EE 容器進行驗證。當使用預先驗證時,Spring Security 必須
-
識別發出請求的使用者。
-
取得使用者的授權。
詳細資訊取決於外部驗證機制。在 X.509 的情況下,使用者可能會透過其憑證資訊來識別,或者在 Siteminder 的情況下,透過 HTTP 請求標頭來識別。如果依賴容器驗證,則透過呼叫傳入 HTTP 請求的 getUserPrincipal()
方法來識別使用者。在某些情況下,外部機制可能會為使用者提供角色和授權資訊。但是,在其他情況下,您必須從單獨的來源取得授權,例如 UserDetailsService
。
預先驗證架構類別
由於大多數預先驗證機制都遵循相同的模式,因此 Spring Security 擁有一組類別,這些類別提供了一個內部框架,用於實作預先驗證的驗證提供者。這消除了重複,並允許以結構化的方式新增新的實作,而無需從頭開始編寫所有內容。如果您想使用 X.509 驗證 之類的功能,則無需了解這些類別,因為它已經有一個命名空間組態選項,使用起來更簡單且更容易上手。如果您需要使用明確的 Bean 組態,或者計劃編寫自己的實作,則需要了解所提供的實作如何運作。您可以在 org.springframework.security.web.authentication.preauth
下找到這些類別。我們在這裡僅提供一個概述,因此您應該查閱 Javadoc 和原始碼以取得適當的資訊。
AbstractPreAuthenticatedProcessingFilter
此類別檢查安全性內容的目前內容,如果內容為空,則嘗試從 HTTP 請求中提取使用者資訊,並將其提交給 AuthenticationManager
。子類別會覆寫以下方法以取得此資訊。
-
Java
-
Kotlin
protected abstract Object getPreAuthenticatedPrincipal(HttpServletRequest request);
protected abstract Object getPreAuthenticatedCredentials(HttpServletRequest request);
protected abstract fun getPreAuthenticatedPrincipal(request: HttpServletRequest): Any?
protected abstract fun getPreAuthenticatedCredentials(request: HttpServletRequest): Any?
在呼叫這些方法之後,篩選器會建立一個 PreAuthenticatedAuthenticationToken
,其中包含傳回的資料,並提交以進行驗證。此處的「驗證」實際上只是指進一步的處理,以便可能載入使用者的授權,但遵循標準的 Spring Security 驗證架構。
如同其他 Spring Security 驗證篩選器,預先驗證篩選器具有 authenticationDetailsSource
屬性,預設情況下,該屬性會建立 WebAuthenticationDetails
物件,以將其他資訊儲存在 Authentication
物件的 details
屬性中,例如工作階段識別碼和來源 IP 位址。如果可以從預先驗證機制取得使用者角色資訊,則資料也會儲存在此屬性中,詳細資訊會實作 GrantedAuthoritiesContainer
介面。這使驗證提供者能夠讀取外部分配給使用者的授權。接下來,我們來看一個具體的範例。
J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource
如果篩選器配置了 authenticationDetailsSource
,它是此類別的實例,則透過針對預先決定的「可對應角色」集合中的每個角色呼叫 isUserInRole(String role)
方法來取得授權資訊。該類別從配置的 MappableAttributesRetriever
取得這些角色。可能的實作包括在應用程式內容中硬式編碼列表,以及從 web.xml
檔案中的 <security-role>
資訊中讀取角色資訊。預先驗證範例應用程式使用後一種方法。
還有一個額外階段,角色(或屬性)透過使用配置的 Attributes2GrantedAuthoritiesMapper
對應到 Spring Security GrantedAuthority
物件。預設值只是將常用的 ROLE_
前綴新增到名稱中,但它讓您可以完全控制行為。
PreAuthenticatedAuthenticationProvider
預先驗證的提供者只需載入使用者的 UserDetails
物件。它透過委派給 AuthenticationUserDetailsService
來完成此操作。後者類似於標準的 UserDetailsService
,但採用 Authentication
物件,而不僅僅是使用者名稱
public interface AuthenticationUserDetailsService {
UserDetails loadUserDetails(Authentication token) throws UsernameNotFoundException;
}
此介面也可能還有其他用途,但是,對於預先驗證,它允許存取封裝在 Authentication
物件中的授權,正如我們在前一節中看到的那樣。PreAuthenticatedGrantedAuthoritiesUserDetailsService
類別執行此操作。或者,它可以透過 UserDetailsByNameServiceWrapper
實作委派給標準的 UserDetailsService
。
Http403ForbiddenEntryPoint
AuthenticationEntryPoint
負責啟動未驗證使用者的驗證程序(當他們嘗試存取受保護的資源時)。但是,在預先驗證的情況下,這不適用。如果您不將預先驗證與其他驗證機制結合使用,則只會使用此類別的實例來配置 ExceptionTranslationFilter
。如果使用者被 AbstractPreAuthenticatedProcessingFilter
拒絕,導致空驗證,則會呼叫它。如果呼叫它,它始終會傳回 403
-forbidden 回應碼。
具體實作
X.509 驗證在其 自己的章節 中介紹。在這裡,我們來看一些類別,這些類別為其他預先驗證情境提供支援。
請求標頭驗證 (Siteminder)
外部驗證系統可以透過在 HTTP 請求上設定特定標頭,向應用程式提供資訊。Siteminder 就是一個眾所周知的範例,它在名為 SM_USER
的標頭中傳遞使用者名稱。RequestHeaderAuthenticationFilter
類別支援此機制,該類別僅從標頭中提取使用者名稱。預設情況下,它使用名稱 SM_USER
作為標頭名稱。請參閱 Javadoc 以取得更多詳細資訊。
當使用像這樣的系統時,框架根本不執行任何驗證檢查,而且極其重要的是,外部系統已正確配置並保護對應用程式的所有存取。如果攻擊者能夠偽造其原始請求中的標頭而未被偵測到,則他們可能會選擇他們想要的任何使用者名稱。 |
Siteminder 範例組態
以下範例顯示了使用此篩選器的典型組態
<security:http>
<!-- Additional http configuration omitted -->
<security:custom-filter position="PRE_AUTH_FILTER" ref="siteminderFilter" />
</security:http>
<bean id="siteminderFilter" class="org.springframework.security.web.authentication.preauth.RequestHeaderAuthenticationFilter">
<property name="principalRequestHeader" value="SM_USER"/>
<property name="authenticationManager" ref="authenticationManager" />
</bean>
<bean id="preauthAuthProvider" class="org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider">
<property name="preAuthenticatedUserDetailsService">
<bean id="userDetailsServiceWrapper"
class="org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper">
<property name="userDetailsService" ref="userDetailsService"/>
</bean>
</property>
</bean>
<security:authentication-manager alias="authenticationManager">
<security:authentication-provider ref="preauthAuthProvider" />
</security:authentication-manager>
我們在這裡假設 安全性命名空間 用於組態。也假設您已將 UserDetailsService
(稱為 "userDetailsService")新增到您的組態中,以載入使用者的角色。
Java EE 容器驗證
J2eePreAuthenticatedProcessingFilter
類別從 HttpServletRequest
的 userPrincipal
屬性中提取使用者名稱。此篩選器的使用通常會與 Java EE 角色的使用結合,如先前在 J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource 中所述。
在程式碼庫中,有一個 範例應用程式 使用了此方法,因此如果您有興趣,請從 Github 取得程式碼並查看應用程式內容檔案。