自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Spring Security 中的 RememberMe 登錄,so easy!

開(kāi)發(fā) 前端
持久化令牌比前面的普通令牌安全系數(shù)提高了不少,但是依然存在風(fēng)險(xiǎn)。安全問(wèn)題和用戶的使用便捷性就像一個(gè)悖論,想要用戶使用方便,不可避免地要犧牲一點(diǎn)安全性。對(duì)于開(kāi)發(fā)者而言,要做的就是如何將系統(tǒng)存在的安全風(fēng)險(xiǎn)降到最低。

?1. RememberMe簡(jiǎn)介

RememberMe 這個(gè)功能非常常見(jiàn),圖 6-1 所示就是 QQ 郵箱登錄時(shí)的“記住我”選項(xiàng)。

圖片

提到 RememberMe,一些初學(xué)者往往會(huì)有一些誤解,認(rèn)為 RememberMe 功能就是把用戶名/密碼用 Cookie 保存在瀏覽器中,下次登錄時(shí)不用再次輸入用戶名/密碼。這個(gè)理解顯然是不對(duì)的。

我們這里所說(shuō)的 RememberMe 是一種服務(wù)器端的行為。傳統(tǒng)的登錄方式基于  Session 會(huì)話,一旦用戶關(guān)閉瀏覽器重新打開(kāi),就要再次登錄,這樣太過(guò)于煩瑣。如果能有一種機(jī)制,讓用戶關(guān)閉并重新打開(kāi)瀏覽器之后,還能繼續(xù)保持認(rèn)證狀態(tài),就會(huì)方便很多,RememberMe 就是為了解決這一需求而生的。

具體的實(shí)現(xiàn)思路就是通過(guò) Cookie 來(lái)記錄當(dāng)前用戶身份。當(dāng)用戶登錄成功之后,會(huì)通過(guò)一定的算法,將用戶信息、時(shí)間戳等進(jìn)行加密,加密完成后,通過(guò)響應(yīng)頭帶回前端存儲(chǔ)在 Cookie 中,當(dāng)瀏覽器關(guān)閉之后重新打開(kāi),如果再次訪問(wèn)該網(wǎng)站,會(huì)自動(dòng)將 Cookie 中的信息發(fā)送給服務(wù)器,服務(wù)器對(duì) Cookie 中的信息進(jìn)行校驗(yàn)分析,進(jìn)而確定出用戶的身份,Cookie 中所保存的用戶信息也是有時(shí)效的,例如三天、一周等。敏銳的讀者可能已經(jīng)發(fā)現(xiàn)這種方式是存在安全隱患的。所謂魚(yú)與熊掌不可兼得,要想使用便利,就要犧牲一定的安全性,不過(guò)在本章中,我們將會(huì)介紹通過(guò)持久化令牌以及二次校驗(yàn)來(lái)降低使用 RememberMe 所帶來(lái)的安全風(fēng)險(xiǎn)。

2. RememberMe基本用法

我們先來(lái)看一種最簡(jiǎn)單的用法。

首先創(chuàng)建一個(gè) Spring Boot 工程,引入 spring-boot-starter-security 依賴。工程創(chuàng)建成功后,添加一個(gè) HelloController 并創(chuàng)建一個(gè)測(cè)試接口,代碼如下:

@RestController
public class HelloController {
@GetMapping("/hello")
public String hello(){
return "hello";
}
}

然后創(chuàng)建 SecurityConfig 配置文件:

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
PasswordEncoder passwordEncoder(){
return NoOpPasswordEncoder.getInstance();
}
@Override
protected void configure(AuthenticationManagerBuilder auth)
throws Exception {
auth.inMemoryAuthentication()
.withUser("javaboy")
.password("123")
.roles("admin");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin()
.and()
.rememberMe()
.key("javaboy")
.and()
.csrf().disable();
}
}

這里我們主要是調(diào)用了 HttpSecurity 中的 rememberMe 方法并配置了一個(gè) key,該方法最終會(huì)向過(guò)濾器鏈中添加 RememberMeAuthenticationFilter 過(guò)濾器。

配置完成后,啟動(dòng)項(xiàng)目,當(dāng)我們?cè)L問(wèn) /hello 接口時(shí),會(huì)自動(dòng)重定向到登錄頁(yè)面,如圖 6-2 所示。

圖片

可以看到,此時(shí)的默認(rèn)登錄頁(yè)面多了一個(gè) RememberMe 選項(xiàng),勾選上 RememberMe,登錄成功之后,我們就可以訪問(wèn) /hello? 接口了。訪問(wèn)完成后,關(guān)閉瀏覽器再重新打開(kāi),此時(shí)不需要登錄就可以直接訪問(wèn) /hello? 接口;同時(shí),如果關(guān)閉掉服務(wù)端重新打開(kāi),再去訪問(wèn) /hello接口,發(fā)現(xiàn)此時(shí)也不需要登錄了。

那么這一切是怎么實(shí)現(xiàn)的呢?打開(kāi)瀏覽器控制臺(tái),我們來(lái)分析整個(gè)登錄過(guò)程。

首先,當(dāng)我們單擊登錄按鈕時(shí),多了一個(gè)請(qǐng)求參數(shù) remember-me,如圖6-3所示。

圖片

很明顯,remember-me 參數(shù)就是用來(lái)告訴服務(wù)端是否開(kāi)啟 RememberMe 功能,如果開(kāi)發(fā)者自定義登錄頁(yè)面,那么默認(rèn)情況下,是否開(kāi)啟 RememberMe 的參數(shù)就是 remember-me。

當(dāng)請(qǐng)求成功后,在響應(yīng)頭中多出了一個(gè) Set-Cookie,如圖 6-4 所示。

圖片

在響應(yīng)頭中給出了一個(gè) remember-me 字符串。以后所有請(qǐng)求的請(qǐng)求頭 Cookie 字段,都會(huì)自動(dòng)攜帶上這個(gè)令牌,服務(wù)端利用該令牌可以校驗(yàn)用戶身份是否合法。

大致的流程就是這樣,但是大家發(fā)現(xiàn)這種方式安全隱患很大,一旦 remember-me 令牌泄漏,惡意用戶就可以拿著這個(gè)令牌去隨意訪問(wèn)系統(tǒng)資源。持久化令牌和二次校驗(yàn)可以在一定程度上降低該問(wèn)題帶來(lái)的風(fēng)險(xiǎn)。

3. 持久化令牌

使用持久化令牌實(shí)現(xiàn) RememberMe 的體驗(yàn)和使用普通令牌的登錄體驗(yàn)是一樣的,不同的是服務(wù)端所做的事情變了。

持久化令牌在普通令牌的基礎(chǔ)上,新增了 series 和 token 兩個(gè)校驗(yàn)參數(shù),當(dāng)使用用戶名/密碼的方式登錄時(shí),series 才會(huì)自動(dòng)更新;而一旦有了新的會(huì)話,token 就會(huì)重新生成。所以,如果令牌被人盜用,一旦對(duì)方基于 RememberMe 登錄成功后,就會(huì)生成新的 token,你自己的登錄令牌就會(huì)失效,這樣就能及時(shí)發(fā)現(xiàn)賬戶泄漏并作出處理,比如清除自動(dòng)登錄令牌、通知用戶賬戶泄漏等。

Spring Security中對(duì)于持久化令牌提供了兩種實(shí)現(xiàn):

  • JdbcTokenRepositoryImpl
  • InMemoryTokenRepositoryImpl

前者是基于 JdbcTemplate 來(lái)操作數(shù)據(jù)庫(kù),后者則是操作存儲(chǔ)在內(nèi)存中的數(shù)據(jù)。由于 InMemoryTokenRepositoryImpl 的使用場(chǎng)景很少,因此這里主要介紹基于 JdbcTokenRepositoryImpl 的配置。

首先創(chuàng)建一個(gè) security06 數(shù)據(jù)庫(kù),然后我們需要一張表來(lái)記錄令牌信息,創(chuàng)建表的 SQL 腳本在在 JdbcTokenRepositoryImpl? 類中的 CREATE_TABLE_SQL 變量上已經(jīng)定義好了,代碼如下:

public static final String CREATE_TABLE_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)";

我們直接將變量中定義的 SQL 腳本拷貝出來(lái)到數(shù)據(jù)庫(kù)中執(zhí)行,生成一張 persistent_logins 表用來(lái)記錄令牌信息。persistent_logins 表一共就四個(gè)字段:username 表示登錄用戶名、series 表示生成的 series 字符串、token 表示生成的 token 字符串、last_used 則表示上次使用時(shí)間。

接下來(lái),在項(xiàng)目中引入 JdbcTemplate 依賴和 MySQL 數(shù)據(jù)庫(kù)驅(qū)動(dòng)依賴:

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>

然后在 application.properties 中配置數(shù)據(jù)庫(kù)連接信息:

spring.datasource.url=jdbc:mysql:///security06?useUnicode=true&characterEncod
ing=UTF-8&serverTimeznotallow=Asia/Shanghai
spring.datasource.username=root
spring.datasource.password=123

最后修改 SecurityConfig:

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
DataSource dataSource;
@Bean
JdbcTokenRepositoryImpl jdbcTokenRepository(){
JdbcTokenRepositoryImpl jdbcTokenRepository =
new JdbcTokenRepositoryImpl();
jdbcTokenRepository.setDataSource(dataSource);
return jdbcTokenRepository;
}
@Bean
PasswordEncoder passwordEncoder(){
return NoOpPasswordEncoder.getInstance();
}
@Override
protected void configure(AuthenticationManagerBuilder auth)
throws Exception {
auth.inMemoryAuthentication()
.withUser("javaboy")
.password("123")
.roles("admin");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin()
.and()
.rememberMe()
.tokenRepository(jdbcTokenRepository())
.and()
.csrf().disable();
}
}

在配置中我們提供了一個(gè) JdbcTokenRepositoryImpl 實(shí)例,并為其配置了數(shù)據(jù)源,最后在配置 RememberMe 時(shí)通過(guò) tokenRepository 方法指定 JdbcTokenRepositoryImpl 實(shí)例。

配置完成后,啟動(dòng)項(xiàng)目并進(jìn)行登錄測(cè)試。登錄成功后,我們發(fā)現(xiàn)數(shù)據(jù)庫(kù)表中多了一條記錄,如圖6-5所示。

圖片

此時(shí)如果關(guān)閉瀏覽器重新打開(kāi),再去訪問(wèn) /hello? 接口,訪問(wèn)時(shí)并不需要登錄,但是訪問(wèn)成功之后,數(shù)據(jù)庫(kù)中的 token? 字段會(huì)發(fā)生變化。同時(shí),如果服務(wù)端重啟之后,瀏覽器再去訪問(wèn) /hello? 接口,依然不需要登錄,但是 token? 字段也會(huì)更新,因?yàn)檫@兩種情況中都有新會(huì)話的建立,所以 token? 會(huì)更新,而 series 則不會(huì)更新。當(dāng)然,如果用戶注銷登錄,則數(shù)據(jù)庫(kù)中和該用戶相關(guān)的登錄記錄會(huì)自動(dòng)清除。

可以看到,持久化令牌比前面的普通令牌安全系數(shù)提高了不少,但是依然存在風(fēng)險(xiǎn)。安全問(wèn)題和用戶的使用便捷性就像一個(gè)悖論,想要用戶使用方便,不可避免地要犧牲一點(diǎn)安全性。對(duì)于開(kāi)發(fā)者而言,要做的就是如何將系統(tǒng)存在的安全風(fēng)險(xiǎn)降到最低。

那么怎么辦呢?二次校驗(yàn)可以幫助我們進(jìn)一步降低風(fēng)險(xiǎn).....

責(zé)任編輯:武曉燕 來(lái)源: 江南一點(diǎn)雨
相關(guān)推薦

2024-04-16 10:09:42

2022-07-27 08:49:34

接口加密解密

2021-04-23 10:38:52

Spring BootSpringMVC源碼

2022-05-06 10:42:09

JavaFlowable引擎

2021-04-23 07:33:10

SpringSecurity單元

2018-09-05 21:07:06

數(shù)據(jù)管理

2012-09-07 09:41:15

Win 8關(guān)機(jī)

2020-12-10 08:21:27

XML映射Mybatis

2019-12-23 10:51:40

Python車票搶票

2022-09-06 08:40:33

應(yīng)用系統(tǒng)登錄方式Spring

2023-04-03 15:04:00

RPCPHP語(yǔ)言

2012-05-18 14:24:57

fedora 17安裝卸載

2021-04-26 08:54:17

Spring BootSecurity防重登錄

2021-05-12 08:32:53

Spring Secu 自定義session

2021-03-04 11:50:48

微信Spring Secu登錄

2019-03-26 11:36:28

網(wǎng)絡(luò)

2021-03-03 08:02:13

JavaScript函數(shù)字節(jié)

2025-02-04 11:18:49

Spring安全應(yīng)用

2021-06-07 14:06:19

Spring SecuCSRF防御

2022-05-05 10:40:36

Spring權(quán)限對(duì)象
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)