基本身份驗證

本節詳細說明 Spring Security 如何為基於 servlet 的應用程式提供 基本 HTTP 身份驗證 的支援。

本節描述 HTTP 基本身份驗證在 Spring Security 中的運作方式。首先,我們看到 WWW-Authenticate 標頭被傳回給未經身份驗證的客戶端

basicauthenticationentrypoint
圖 1. 傳送 WWW-Authenticate 標頭

前圖建立在我們的 SecurityFilterChain 圖表之上。

number 1 首先,使用者對資源 /private 發出未經身份驗證的請求,但該資源未經授權。

number 2 Spring Security 的 AuthorizationFilter 指出未經身份驗證的請求被拒絕 (Denied),並拋出 AccessDeniedException

number 3 由於使用者未經身份驗證,ExceptionTranslationFilter 啟動開始身份驗證 (Start Authentication)。已組態的 AuthenticationEntryPointBasicAuthenticationEntryPoint 的實例,它會傳送 WWW-Authenticate 標頭。RequestCache 通常是 NullRequestCache,不會儲存請求,因為客戶端能夠重播其原始請求的請求。

當客戶端收到 WWW-Authenticate 標頭時,它知道應該使用使用者名稱和密碼重試。下圖顯示了處理使用者名稱和密碼的流程

basicauthenticationfilter
圖 2. 驗證使用者名稱和密碼

前圖建立在我們的 SecurityFilterChain 圖表之上。

number 1 當使用者提交其使用者名稱和密碼時,BasicAuthenticationFilter 會建立 UsernamePasswordAuthenticationToken,這是一種 Authentication,方法是從 HttpServletRequest 中提取使用者名稱和密碼。

number 2 接下來,UsernamePasswordAuthenticationToken 會傳遞到 AuthenticationManager 進行身份驗證。AuthenticationManager 的具體外觀取決於 使用者資訊的儲存方式

number 3 如果身份驗證失敗,則為失敗 (Failure)。

  1. SecurityContextHolder 已清除。

  2. 調用 RememberMeServices.loginFail。如果未組態「記住我」功能,則此為空操作 (no-op)。請參閱 Javadoc 中的 RememberMeServices 介面。

  3. 調用 AuthenticationEntryPoint 以再次觸發傳送 WWW-Authenticate。請參閱 Javadoc 中的 AuthenticationEntryPoint 介面。

number 4 如果身份驗證成功,則為成功 (Success)。

  1. Authentication 設定在 SecurityContextHolder 上。

  2. 調用 RememberMeServices.loginSuccess。如果未組態「記住我」功能,則此為空操作 (no-op)。請參閱 Javadoc 中的 RememberMeServices 介面。

  3. BasicAuthenticationFilter 調用 FilterChain.doFilter(request,response) 以繼續執行應用程式邏輯的其餘部分。請參閱 Javadoc 中 BasicAuthenticationFilter 類別

預設情況下,Spring Security 的 HTTP 基本身份驗證支援已啟用。但是,一旦提供任何基於 servlet 的組態,就必須明確提供 HTTP 基本身份驗證。

以下範例顯示了最小的明確組態

明確 HTTP 基本組態
  • Java

  • XML

  • Kotlin

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) {
	http
		// ...
		.httpBasic(withDefaults());
	return http.build();
}
<http>
	<!-- ... -->
	<http-basic />
</http>
@Bean
open fun filterChain(http: HttpSecurity): SecurityFilterChain {
	http {
		// ...
		httpBasic { }
	}
	return http.build()
}