記住我 認證
記住我(remember-me)或持久化登入(persistent-login)認證是指網站能夠在不同會話之間記住使用者的身份。這通常透過向瀏覽器傳送一個 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
通常會自動選擇。如果您的應用程式上下文中有多個,則需要使用 user-service-ref
屬性指定應使用哪一個,其值是您的 UserDetailsService
bean 的名稱。
持久化令牌方法
這種方法基於文章 Improved Persistent Login Cookie Best Practice,並做了一些小修改 [1]。要在名稱空間配置中使用這種方法,您需要提供一個數據源引用
<http>
...
<remember-me data-source-ref="someDataSource"/>
</http>
資料庫應包含一個 persistent_logins
表,使用以下 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
處理。認證提供者和 TokenBasedRememberMeServices
之間共享一個 key
。此外,TokenBasedRememberMeServices
需要一個 UserDetailsService
,從中可以檢索使用者名稱和密碼用於簽名比較,並生成包含正確 GrantedAuthority
例項的 RememberMeAuthenticationToken
。TokenBasedRememberMeServices
也實現了 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
,將令牌儲存在資料庫中。
有關資料庫模式,請參閱持久化令牌方法。