1. 概述
本文將介紹如何設置 Spring Security 中的 記住我功能 – 不僅使用標準 cookie 方式,還 採用更安全的方法,使用持久化。
作為快速介紹 – Spring 可以配置為在瀏覽器會話之間記住登錄憑據。這允許您登錄到網站,然後在訪問該網站時自動為您登錄(即使在您關閉瀏覽器期間)。
2. 兩種“記住我”解決方案
Spring 提供兩種略有不同的實現來解決這個問題。兩者都使用 UsernamePasswordAuthenticationFilter,通過鈎子調用 RememberMeServices 實現。
我們之前已經介紹了標準“記住我”解決方案,它僅使用一個 cookie,在之前的文章中,這個 cookie 叫做 remember-me,包含用户名、過期時間以及包含密碼的 MD5 哈希值。由於它包含密碼的哈希值,該解決方案在 cookie 被捕獲時可能存在漏洞
考慮到這一點,讓我們來查看第二個方法,使用 PersistentTokenBasedRememberMeServices 在會話之間將持久登錄信息存儲在數據庫表中。
3. 前提條件 – 創建數據庫表
首先,我們需要在數據庫中擁有登錄信息——我們需要創建一個表來存儲數據:
create table if not exists persistent_logins (
username varchar_ignorecase(100) not null,
series varchar(64) primary key,
token varchar(64) not null,
last_used timestamp not null
);
這將在啓動時自動創建,通過以下 XML 配置(使用內存中的 H2 數據庫):
<!-- create H2 embedded database table on startup -->
<jdbc:embedded-database id="dataSource" type="H2">
<jdbc:script location="classpath:/persisted_logins_create_table.sql"/>
</jdbc:embedded-database>
為了完整性,這裏是持久化設置的方式:
@Configuration
@EnableTransactionManagement
@PropertySource({ "classpath:persistence-h2.properties" })
public class DatabaseConfig {
@Autowired private Environment env;
@Bean
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(env.getProperty("jdbc.driverClassName"));
dataSource.setUrl(env.getProperty("jdbc.url"));
dataSource.setUsername(env.getProperty("jdbc.user"));
dataSource.setPassword(env.getProperty("jdbc.pass"));
return dataSource;
}
}
4. Spring Security 配置
第一個關鍵配置是 Remember-Me Http 配置(注意 dataSource 屬性):
<http use-expressions="true">
...
<remember-me data-source-ref="dataSource" token-validity-seconds="86400"/>
<http">
接下來,我們需要配置實際的 RememberMeService 和 JdbcTokenRepository (它也使用了 dataSource):
<!-- 永久 Remember Me 服務 -->
<beans:bean id="rememberMeAuthenticationProvider" class=
"org.springframework.security.web.authentication.rememberme.PersistentTokenBasedRememberMeServices">
<beans:constructor-arg value="myAppKey" />
<beans:constructor-arg ref="jdbcTokenRepository" />
<beans:constructor-arg ref="myUserDetailsService" />
</beans:bean>
<!-- 使用數據庫表來維護一組持久登錄數據 -->
<beans:bean id="jdbcTokenRepository"
class="org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl">
<beans:property name="createTableOnStartup" value="false" />
<beans:property name="dataSource" ref="dataSource" />
</beans:bean>
<!-- 身份驗證管理器 (使用與 RememberMeService相同的 UserDetailsService)-->
<authentication-manager alias="authenticationManager">
<authentication-provider user-service-ref="myUserDetailsService"/>
</authentication-provider>
</authentication-manager>
5. Cookie
如我們所説,標準 TokenBasedRememberMeServices 在 Cookie 中存儲了哈希後的用户密碼。
這個解決方案——PersistentTokenBasedRememberMeServices 使用一個 唯一的系列標識符用於用户。該標識符識別用户的初始登錄,並且在用户在持久會話期間自動登錄時保持不變。它還包含 一個隨機令牌,該令牌在用户通過持久的記住我功能登錄時會重新生成。
這種隨機系列和令牌的組合被持久化,使得暴力破解攻擊非常不可能。
6. 在實踐中
要查看記住我機制在瀏覽器中的工作,您可以:
- 使用記住我功能登錄
- 關閉瀏覽器
- 重新打開瀏覽器並返回同一頁面。刷新。
- 您仍然會登錄
未啓用記住我,當 cookie 過期時,用户應被重定向回登錄頁面。啓用記住我,用户現在藉助新的 token/cookie 保持登錄狀態。
您還可以查看瀏覽器中的 cookie,以及在數據庫中的持久化數據(請注意,您可能需要從嵌入式 H2 實現中切換)。
7. 結論
本教程演示瞭如何設置和配置數據庫持久化的“記住我”令牌功能。它也是對先前文章的補充,該文章討論了基於標準 Cookie 令牌的功能。數據庫方法更安全,因為密碼詳情不會保存在 Cookie 中——但它也需要稍微更多的配置。