之前幾篇博客記錄了一下SpringSecurity
一些基礎知識,也在整合Security
那篇博客中記錄了remember-me的使用方法和一些基本原理。這一篇就來記錄一個和remember-me有關的一個操作,採用持久化token的方式來實現remember-me
本次記錄是在原有代碼上改動(實現好普通remember-me)功能的代碼
小前言
以前我們實現remember-me
功能時,是直接在配置類相應方法中加入這一行代碼:
http.rememberMe();
之後就會在登錄頁面自動生成這一個單選框。
當然如果我們需要自定義登錄頁的話,只需要在前端頁面添加一個radio
的單選框,name爲remember-me,value爲true即可
問題剖析
上述的實現原理大致是用戶勾選該單選框之後,Security會爲我們在cookie中保存一個鍵值對,鍵爲remember-me,值爲系統生成的token
。這個token
是系統根據用戶名和密碼自動生成的,同時會設置上過期時間,只要我們在過期時間前訪問頁面,我們都會帶上這個cookie,而系統會根據token
的值判斷該用戶是否已經登錄。
這是有一個問題,我們每次請求時,系統都會給我們帶回這個cookie信息,我們請求別的頁面時,會自動帶回給系統。但是如果有人惡意抓取了這個cookie信息,獲得了token
那麼他就可以僞造用戶用戶去登錄了,這樣就有隱患。而且還有一種隱患,token
是根據用戶名和密碼加密生成的,萬一加密的渠道被破解了,那就等於變相把用戶名和密碼告訴別人了,所以我們需要採取另外一種方式來實現remember-me功能。
正文
這時我們採用持久化token
的方式來存儲token。
在客戶端的cookie中,僅保存一個無意義的加密串(與用戶名、密碼等敏感數據無關),然後在數據庫中保存該加密串-用戶信息的對應關係,自動登錄時,用cookie中的加密串,到db中驗證,如果通過,自動登錄纔算通過。
具體怎麼做呢?
首先需要在對應數據庫中創建一個表,這個表的表名和字段都是寫死的,不要改動。估計可能是底層sql語句就是固定寫好的。
CREATE TABLE persistent_logins (
username varchar(64) not null,
series varchar(64) not null,
token varchar(64) not null,
last_used timestamp not null,
PRIMARY KEY (series)
);
創建好表之後,在配置類中注入一個數據庫連接池類:
@Autowired
private DataSource dataSource;
這裏因爲要持久化token,引入一個類jdbcTokenRepository
,這個類實現了一個接口persistentTokenRepository
,我們點進去這個接口:
public interface PersistentTokenRepository {
void createNewToken(PersistentRememberMeToken var1);
void updateToken(String var1, String var2, Date var3);
PersistentRememberMeToken getTokenForSeries(String var1);
void removeUserTokens(String var1);
}
看方法名字,不難發現這個接口實際上是對token進行增刪改查的。平時默認Security中會自動注入一個該接口的實現類InMemoryTokenRepositoryImpl
,這個類存儲token是在內存中的。就是上文介紹的實現remember-me的方法,這個時候我們需要往ioc容器中注入另外一個接口實現類JdbcTokenRepositoryImpl
,這個作用就是將token存儲在數據庫中。
在配置類中注入組件:
//注入持久化token的組件
@Bean
public PersistentTokenRepository persistentTokenRepository() {
JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
jdbcTokenRepository.setDataSource(dataSource);
return jdbcTokenRepository;
最後一步,指定使用的token存儲器(我這麼叫的.):
http.rememberMe().tokenRepository(persistentTokenRepository()).tokenValiditySeconds(1209600);
第二個方法tokenRepository(persistentTokenRepository())
就是指定token存儲器,tokenValiditySeconds(1209600)
是設定過期時間。
我們登錄測試一下
去數據庫剛剛創建好的表中,可以看到裏面已經有數據了。
至此,我們的token就存儲在數據庫中,這樣安全性也得到了提高。