Remember-Me 驗證
Remember-me 或持久登入驗證指的是網站能夠在工作階段之間記住主體的身份。這通常是透過傳送 Cookie 到瀏覽器來完成,Cookie 會在未來的工作階段中被偵測到,並導致自動登入發生。Spring Security 提供了這些操作發生的必要掛鉤,並且有兩個具體的 remember-me 實作。其中一個使用雜湊來保護基於 Cookie 的權杖的安全性,另一個則使用資料庫或其他持久儲存機制來儲存產生的權杖。
請注意,這兩個實作都需要 UserDetailsService
。如果您使用的驗證提供者未使用 UserDetailsService
(例如,LDAP 提供者),除非您的應用程式內容中也有 UserDetailsService
bean,否則它將無法運作。
簡單的基於雜湊權杖方法
此方法使用雜湊來實現有用的 remember-me 策略。本質上,在成功互動式驗證後,會傳送一個 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 權杖僅在指定的期間內有效,且僅在使用者名稱、密碼和金鑰未變更的情況下有效。值得注意的是,這存在潛在的安全性問題,因為捕獲的 remember-me 權杖可以從任何使用者代理程式使用,直到權杖過期為止。這與摘要式驗證的問題相同。如果主體意識到權杖已被捕獲,他們可以輕鬆變更其密碼,並立即讓所有已發出的 remember-me 權杖失效。如果需要更高的安全性,您應該使用下一節中描述的方法。或者,根本不應使用 remember-me 服務。
如果您熟悉關於命名空間組態章節中討論的主題,您可以透過新增 <remember-me>
元素來啟用 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)
Remember-Me 介面和實作
Remember-me 與 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()
方法。autoLogin()
方法由 RememberMeAuthenticationFilter
在 SecurityContextHolder
不包含 Authentication
時調用。因此,此介面為底層 remember-me 實作提供了足夠的驗證相關事件通知,並在候選 Web 請求可能包含 Cookie 並希望被記住時委派給實作。此設計允許任意數量的 remember-me 實作策略。
我們稍早已經看到 Spring Security 提供了兩個實作。接下來我們將依序查看每個實作。
TokenBasedRememberMeServices
此實作支援 簡單的基於雜湊權杖方法 中描述的更簡單方法。TokenBasedRememberMeServices
產生一個 RememberMeAuthenticationToken
,由 RememberMeAuthenticationProvider
處理。key
在此驗證提供者和 TokenBasedRememberMeServices
之間共享。此外,TokenBasedRememberMeServices
需要一個 UserDetailsService
,它可以從中檢索使用者名稱和密碼以進行簽名比較,並產生 RememberMeAuthenticationToken
以包含正確的 GrantedAuthority
實例。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 才能啟用 remember-me 服務
-
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()
屬性,在您的 AuthenticationManager.setProviders()
清單中包含 RememberMeAuthenticationProvider
,並將 RememberMeAuthenticationFilter
新增到您的 FilterChainProxy
中(通常緊接在您的 UsernamePasswordAuthenticationFilter
之後)。
PersistentTokenBasedRememberMeServices
您可以像使用 TokenBasedRememberMeServices
一樣使用此類別,但它還需要使用 PersistentTokenRepository
進行組態以儲存權杖。
-
InMemoryTokenRepositoryImpl
僅用於測試。 -
JdbcTokenRepositoryImpl
將權杖儲存在資料庫中。
請參閱 持久權杖方法 以取得資料庫結構描述。