預認證場景

示例包括 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。子類覆蓋以下方法以獲取此資訊。

覆蓋 AbstractPreAuthenticatedProcessingFilter
  • 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 類實現了這一點。另外,它也可以透過 UserDetailsByNameServiceServiceWrapper 實現委託給標準的 UserDetailsService

Http403ForbiddenEntryPoint

AuthenticationEntryPoint 負責啟動未認證使用者的認證過程(當他們嘗試訪問受保護資源時)。但在預認證情況下,這不適用。只有當您不將預認證與其他認證機制結合使用時,才會配置 ExceptionTranslationFilter 來使用此類的一個例項。如果使用者被 AbstractPreAuthenticatedProcessingFilter 拒絕,導致認證為空,則會呼叫它。如果被呼叫,它總是返回 403 禁止響應碼。

具體實現

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 類從 HttpServletRequestuserPrincipal 屬性中提取使用者名稱。使用此過濾器通常會與 Java EE 角色的使用結合,如前面J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource 中所述。

在程式碼庫中有一個使用此方法的示例應用程式,如果您有興趣,可以從 Github 獲取程式碼並檢視其應用程式上下文檔案。