記住我認證

“記住我”或“持久登入”認證是指網站能夠在會話之間記住主體的身份。這通常透過向瀏覽器傳送一個 cookie 來實現,該 cookie 在未來的會話中被檢測到並導致自動登入。Spring Security 為這些操作提供了必要的鉤子,並具有兩種具體的“記住我”實現。一種使用雜湊來保持基於 cookie 的令牌的安全性,另一種使用資料庫或其他持久儲存機制來儲存生成的令牌。

請注意,兩種實現都需要 UserDetailsService。如果您使用的認證提供程式不使用 UserDetailsService(例如,LDAP 提供程式),除非您的應用程式上下文中也有一個 UserDetailsService bean,否則它將不起作用。

簡單基於雜湊的令牌方法

此方法使用雜湊來實現有用的“記住我”策略。本質上,在成功的互動式認證後,一個 cookie 被髮送到瀏覽器,該 cookie 的構成如下

base64(username + ":" + expirationTime + ":" + algorithmName + ":"
algorithmHex(username + ":" + expirationTime + ":" password + ":" + key))

username:          As identifiable to the UserDetailsService
password:          That matches the one in the retrieved UserDetails
expirationTime:    The date and time when the remember-me token expires, expressed in milliseconds
key:               A private key to prevent modification of the remember-me token
algorithmName:     The algorithm used to generate and to verify the remember-me token signature

“記住我”令牌僅在指定的時間段內有效,並且僅當用戶名、密碼和金鑰不變時才有效。值得注意的是,這存在一個潛在的安全問題,即在令牌過期之前,被截獲的“記住我”令牌可以從任何使用者代理使用。這與摘要認證的問題相同。如果主體意識到令牌已被截獲,他們可以輕鬆更改密碼並立即使所有已發行的“記住我”令牌失效。如果需要更重要的安全性,您應該使用下一節中描述的方法。或者,根本不應使用“記住我”服務。

如果您熟悉 名稱空間配置 一章中討論的主題,您可以透過新增 <remember-me> 元素來啟用“記住我”認證

<http>
...
<remember-me key="myAppKey"/>
</http>

UserDetailsService 通常會自動選擇。如果您的應用程式上下文中有一個以上的 UserDetailsService,則需要使用 user-service-ref 屬性指定要使用的那個,其中值是您的 UserDetailsService bean 的名稱。

持久令牌方法

此方法基於文章 改進的持久登入 Cookie 最佳實踐,並進行了一些細微修改 [1]。要將此方法與名稱空間配置一起使用,您需要提供一個數據源引用

<http>
...
<remember-me data-source-ref="someDataSource"/>
</http>

資料庫應包含一個 persistent_logins 表,使用以下 SQL(或等效 SQL)建立

create table persistent_logins (username varchar(64) not null,
								series varchar(64) primary key,
								token varchar(64) not null,
								last_used timestamp not null)

記住我介面和實現

“記住我”與 UsernamePasswordAuthenticationFilter 一起使用,並透過 AbstractAuthenticationProcessingFilter 超類中的鉤子實現。它也用於 BasicAuthenticationFilter 中。這些鉤子在適當的時候呼叫具體的 RememberMeServices。以下列表顯示了介面

Authentication autoLogin(HttpServletRequest request, HttpServletResponse response);

void loginFail(HttpServletRequest request, HttpServletResponse response);

void loginSuccess(HttpServletRequest request, HttpServletResponse response,
	Authentication successfulAuthentication);

有關方法功能的更詳細討論,請參閱 RememberMeServices 的 Javadoc,但請注意,在此階段,AbstractAuthenticationProcessingFilter 僅呼叫 loginFail()loginSuccess() 方法。每當 SecurityContextHolder 不包含 Authentication 時,RememberMeAuthenticationFilter 都會呼叫 autoLogin() 方法。因此,此介面為底層“記住我”實現提供了足夠的認證相關事件通知,並在候選 Web 請求可能包含 cookie 並希望被記住時委託給實現。這種設計允許任意數量的“記住我”實現策略。

我們前面已經看到 Spring Security 提供了兩種實現。我們依次檢視它們。

TokenBasedRememberMeServices

此實現支援 簡單基於雜湊的令牌方法 中描述的更簡單的方法。TokenBasedRememberMeServices 生成一個 RememberMeAuthenticationToken,由 RememberMeAuthenticationProvider 處理。一個 key 在此認證提供程式和 TokenBasedRememberMeServices 之間共享。此外,TokenBasedRememberMeServices 需要一個 UserDetailsService,從中它可以檢索使用者名稱和密碼以進行簽名比較,並生成包含正確 GrantedAuthority 例項的 RememberMeAuthenticationTokenTokenBasedRememberMeServices 還實現了 Spring Security 的 LogoutHandler 介面,因此它可以與 LogoutFilter 一起使用以自動清除 cookie。

預設情況下,此實現使用 SHA-256 演算法對令牌簽名進行編碼。為了驗證令牌簽名,會解析並使用從 algorithmName 檢索到的演算法。如果沒有 algorithmName,則將使用預設匹配演算法,即 SHA-256。您可以為簽名編碼和簽名匹配指定不同的演算法,這允許使用者安全地升級到不同的編碼演算法,同時在沒有 algorithmName 的情況下仍然能夠驗證舊演算法。為此,您可以將自定義的 TokenBasedRememberMeServices 指定為 Bean 並在配置中使用它。

  • Java

  • XML

@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http, RememberMeServices rememberMeServices) throws Exception {
	http
			.authorizeHttpRequests((authorize) -> authorize
					.anyRequest().authenticated()
			)
			.rememberMe((remember) -> remember
				.rememberMeServices(rememberMeServices)
			);
	return http.build();
}

@Bean
RememberMeServices rememberMeServices(UserDetailsService userDetailsService) {
	RememberMeTokenAlgorithm encodingAlgorithm = RememberMeTokenAlgorithm.SHA256;
	TokenBasedRememberMeServices rememberMe = new TokenBasedRememberMeServices(myKey, userDetailsService, encodingAlgorithm);
	rememberMe.setMatchingAlgorithm(RememberMeTokenAlgorithm.MD5);
	return rememberMe;
}
<http>
  <remember-me services-ref="rememberMeServices"/>
</http>

<bean id="rememberMeServices" class=
"org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices">
    <property name="userDetailsService" ref="myUserDetailsService"/>
    <property name="key" value="springRocks"/>
    <property name="matchingAlgorithm" value="MD5"/>
    <property name="encodingAlgorithm" value="SHA256"/>
</bean>

在應用程式上下文中需要以下 bean 才能啟用“記住我”服務

  • Java

  • XML

@Bean
RememberMeAuthenticationFilter rememberMeFilter() {
    RememberMeAuthenticationFilter rememberMeFilter = new RememberMeAuthenticationFilter();
    rememberMeFilter.setRememberMeServices(rememberMeServices());
    rememberMeFilter.setAuthenticationManager(theAuthenticationManager);
    return rememberMeFilter;
}

@Bean
TokenBasedRememberMeServices rememberMeServices() {
    TokenBasedRememberMeServices rememberMeServices = new TokenBasedRememberMeServices();
    rememberMeServices.setUserDetailsService(myUserDetailsService);
    rememberMeServices.setKey("springRocks");
    return rememberMeServices;
}

@Bean
RememberMeAuthenticationProvider rememberMeAuthenticationProvider() {
    RememberMeAuthenticationProvider rememberMeAuthenticationProvider = new RememberMeAuthenticationProvider();
    rememberMeAuthenticationProvider.setKey("springRocks");
    return rememberMeAuthenticationProvider;
}
<bean id="rememberMeFilter" class=
"org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter">
<property name="rememberMeServices" ref="rememberMeServices"/>
<property name="authenticationManager" ref="theAuthenticationManager" />
</bean>

<bean id="rememberMeServices" class=
"org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices">
<property name="userDetailsService" ref="myUserDetailsService"/>
<property name="key" value="springRocks"/>
</bean>

<bean id="rememberMeAuthenticationProvider" class=
"org.springframework.security.authentication.RememberMeAuthenticationProvider">
<property name="key" value="springRocks"/>
</bean>

請記住將您的 RememberMeServices 實現新增到 UsernamePasswordAuthenticationFilter.setRememberMeServices() 屬性中,將 RememberMeAuthenticationProvider 包含在您的 AuthenticationManager.setProviders() 列表中,並將 RememberMeAuthenticationFilter 新增到您的 FilterChainProxy 中(通常緊隨 UsernamePasswordAuthenticationFilter 之後)。

PersistentTokenBasedRememberMeServices

您可以像使用 TokenBasedRememberMeServices 一樣使用此類,但它還需要配置一個 PersistentTokenRepository 來儲存令牌。

  • InMemoryTokenRepositoryImpl 僅用於測試。

  • JdbcTokenRepositoryImpl 將令牌儲存在資料庫中。

請參閱 持久令牌方法 以瞭解資料庫架構。


1. 本質上,cookie 中不包含使用者名稱,以防止不必要地暴露有效的登入名。對此在本文的評論部分有討論。
© . This site is unofficial and not affiliated with VMware.