成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

一個奇怪的登錄需求,你知道嗎?

開發 前端
今天就和小伙伴們分享了一下在 Spring Security 中如何拋出 UsernameNotFoundException 異常,雖然這只是一個小眾需求,但是可以加深大家對 Spring Security 的理解。

一個奇怪的登錄需求。

這是小伙伴們在微信群里的一個提問,我覺得很有意思:

雖然這并非一個典型需求,但是把這個問題解決了,有助于加深大家對于 Spring Security 的理解。

因此,松哥打算擼一篇文章和大家稍微聊聊這個話題。

1. 問題再現

可能有小伙伴還不明白這個問題,因此我先稍微解釋一下。

當我們登錄失敗的時候,可能用戶名寫錯,也可能密碼寫錯,但是出于安全考慮,服務端一般不會明確提示是用戶名寫錯了還是密碼寫錯了,而只會給出一個模糊的用戶名或者密碼寫錯了。

然而對于很多新手程序員而言,可能并不了解這樣一些“潛規則”,可能會給用戶一個明確的提示,明確提示是用戶名寫錯了還是密碼寫錯了。

為了避免這一情況,Spring Security 通過封裝,隱藏了用戶名不存在的異常,導致開發者在開發的時候,只能獲取到 BadCredentialsException,這個異常既表示用戶名不存在,也表示用戶密碼輸入錯誤。Spring Security 這樣做是為了確保我們的系統足夠安全。

然而由于種種原因,有時候我們又希望能夠分別獲取到用戶不存在的異常和密碼輸入錯誤的異常,這個時候就需要我們對 Spring Security 進行一些簡單的定制了。

2. 源碼分析

首先我們要先找到問題發生的原因,發生的地方。

在 Spring Security 中,負責用戶校驗的工作的類有很多,我這里就不一一列舉了(感興趣的小伙伴可以查看《深入淺出Spring Security》一書),我這里直接說我們涉及到的關鍵類 AbstractUserDetailsAuthenticationProvider。

這個類將負責用戶名密碼的校驗工作,具體在 authenticate 方法里邊,這個方法本來特別長,我這里只把和本文相關的代碼列出來:

@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
try {
user = retrieveUser(username, (UsernamePasswordAuthenticationToken) authentication);
}
catch (UsernameNotFoundException ex) {
if (!this.hideUserNotFoundExceptions) {
throw ex;
}
throw new BadCredentialsException(this.messages
.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
}
}
}

retrieveUser 方法就是根據用戶登錄輸入的用戶名去查找用戶,如果沒找到,就會拋出一個 UsernameNotFoundException,這個異常被 catch 之后,會首先判斷是否要隱藏這個異常,如果不隱藏,則原異常原封不動拋出來,如果需要隱藏,則拋出一個新的 BadCredentialsException 異常,BadCredentialsException 異常從字面理解就是密碼輸入錯誤的異常。

所以問題的核心就變成了 hideUserNotFoundExceptions 變量了。

這是一個 Boolean 類型的屬性,默認是 true,AbstractUserDetailsAuthenticationProvider 也為該屬性提供了 set 方法:

public void setHideUserNotFoundExceptions(boolean hideUserNotFoundExceptions) {
this.hideUserNotFoundExceptions = hideUserNotFoundExceptions;
}

看起來修改 hideUserNotFoundExceptions 屬性并不難!只要找到 AbstractUserDetailsAuthenticationProvider 的實例,然后調用相應的 set 方法就能修改了。

現在問題的核心變成了從哪里獲取 AbstractUserDetailsAuthenticationProvider 的實例?

看名字就知道,AbstractUserDetailsAuthenticationProvider 是一個抽象類,所以它的實例其實就是它子類的實例,子類是誰?當然是負責用戶密碼校驗工作的 DaoAuthenticationProvider。

這個知識點先記住,我們一會會用到。

3. 登錄流程

為了弄明白這個問題,我們還需要搞懂 Spring Security 一個大致的認證流程,這個也非常重要。

首先大家知道,Spring Security 的認證工作主要是由 AuthenticationManager 來完成的,而 AuthenticationManager 則是一個接口,它的實現類是 ProviderManager。簡而言之,Spring Security 中具體負責校驗工作的是 ProviderManager#authenticate 方法。

但是校驗工作并不是由 ProviderManager 直接完成的,ProviderManager 中管理了若干個 AuthenticationProvider,ProviderManager 會調用它所管理的 AuthenticationProvider 去完成校驗工作,如下圖:

另一方面,ProviderManager 又分為全局的和局部的。

當我們登錄的時候,首先由局部的 ProviderManager 出場進行用戶名密碼的校驗工作,如果校驗成功,那么用戶就登錄成功了,如果校驗失敗,則會調用局部 ProviderManager 的 parent,也就是全局 ProviderManager 去完成校驗工作,如果全局 ProviderManager 校驗成功,就表示用戶登錄成功,如果全局 ProviderManager 校驗失敗,就表示用戶登錄失敗,如下圖:

OK,有了上面的知識儲備,我們再來分析一下我們想要拋出 UsernameNotFoundException 該怎么做。

4. 思路分析

首先我們的用戶校驗工作在局部的 ProviderManager 中進行,局部的 ProviderManager 中管理了若干個 AuthenticationProvider,這若干個 AuthenticationProvider 中就有可能包含了我們所需要的 DaoAuthenticationProvider。那我們是否需要在這里調用 DaoAuthenticationProvider 的 setHideUserNotFoundExceptions 方法完成屬性的修改呢?

松哥的建議是沒必要!

為什么?

因為當用戶登錄的時候,首先去局部的 ProviderManager 中去校驗,如果校驗成功當然最好;如果校驗失敗,并不會立馬拋出異常,而是去全局的 ProviderManager 中繼續校驗,這樣即使我們在局部 ProviderManager 中拋出了 UsernameNotFoundException 也沒用,因為最終這個異常能不能拋出來決定權在全局 ProviderManager 中(如果全局的 ProviderManager 所管理的 DaoAuthenticationProvider 沒做任何特殊處理,那么局部 ProviderManager 中拋出來的 UsernameNotFoundException 異常最終還是會被隱藏)。

所以,我們要做的就是獲取全局的 ProviderManager,進而獲取到全局 ProviderManager 所管理的 DaoAuthenticationProvider,然后調用其 setHideUserNotFoundExceptions 方法修改相應屬性值即可。

弄明白了原理,代碼就簡單了。

5. 具體實踐

全局 ProviderManager 的修改在 WebSecurityConfigurerAdapter#configure(AuthenticationManagerBuilder) 類中,這里配置的 AuthenticationManagerBuilder 最終用來生成全局的 ProviderManager,所以我們的配置如下:

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
daoAuthenticationProvider.setHideUserNotFoundExceptions(false);
InMemoryUserDetailsManager userDetailsService = new InMemoryUserDetailsManager();
userDetailsService.createUser(User.withUsername("javaboy").password("{noop}123").roles("admin").build());
daoAuthenticationProvider.setUserDetailsService(userDetailsService);
auth.authenticationProvider(daoAuthenticationProvider);
}

@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest().authenticated()
.and()
.formLogin()
.failureHandler((request, response, exception) -> System.out.println(exception))
.permitAll();

}

}

這里的代碼就簡單了:

  • 創建一個 DaoAuthenticationProvider 對象。
  • 調用 DaoAuthenticationProvider 對象的 setHideUserNotFoundExceptions 方法,修改相應的屬性值。
  • 為 DaoAuthenticationProvider 配置用戶數據源。
  • 將 DaoAuthenticationProvider 設置給 auth 對象,auth 將用來生成全局的 ProviderManager。
  • 在另一個 configure 方法中,我們就配置一下登錄回調即可,登錄失敗的時候,打印異常信息看看。

行啦。

接下來啟動項目進行測試。輸入一個錯誤的用戶名,可以看到 IDEA 控制臺會打印出如下信息:

可以看到,UsernameNotFoundException 異常已經拋出來了。

6. 小結

好啦,今天就和小伙伴們分享了一下在 Spring Security 中如何拋出 UsernameNotFoundException 異常,雖然這只是一個小眾需求,但是可以加深大家對 Spring Security 的理解,感興趣的小伙伴可以仔細琢磨下。

責任編輯:武曉燕 來源: 江南一點雨
相關推薦

2024-08-19 09:07:09

TSvoid類型

2023-12-12 08:41:01

2021-11-02 22:50:10

鼠標計算機傳感器

2015-10-23 09:34:16

2025-02-14 10:13:55

2024-09-18 07:00:00

消息隊列中間件消息隊列

2021-10-14 06:52:47

算法校驗碼結構

2022-09-29 15:32:58

云計算計算模式

2024-04-15 00:04:00

APP開發

2024-05-28 09:12:10

2024-04-07 00:00:00

ESlint命令變量

2021-11-17 11:03:14

Python代碼語法

2024-08-01 17:34:56

Promiseaxios請求

2019-12-12 09:23:29

Hello World操作系統函數庫

2022-03-10 08:25:27

JavaScrip變量作用域

2021-09-13 19:28:42

JavaNetty開發

2023-12-20 08:23:53

NIO組件非阻塞

2024-04-30 09:02:48

2023-04-26 10:21:04

2020-11-10 10:26:16

串口打印工具
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 美女黄视频网站 | 亚洲精品久久久久久久久久久久久 | 国产精品久久久久无码av | 男人电影天堂 | 国产精品1区2区3区 男女啪啪高潮无遮挡免费动态 | 人人鲁人人莫人人爱精品 | 国产成人一区二区三区 | 亚洲精品视频在线观看免费 | 国产视频不卡一区 | 国产欧美精品一区二区 | 亚洲精品一区二区冲田杏梨 | 91精品久久久久 | 中文视频在线 | 日本在线看片 | 免费观看的黄色网址 | 祝你幸福电影在线观看 | 91新视频| 日韩欧美精品一区 | 久久久久久久av麻豆果冻 | 亚洲人成在线播放 | 久久免费视频在线 | 久久久久久国产精品 | 精品欧美一区二区精品久久 | 日韩久久久久久 | 国产精品久久久久久吹潮日韩动画 | 亚洲成人第一页 | 欧美男人亚洲天堂 | 五月网婷婷| 麻豆久久久久 | 日韩电影一区二区三区 | 国产97在线 | 日韩 | 色爱av| 国产精品中文字幕在线观看 | 日本久久久一区二区三区 | 综合色在线 | 91在线一区 | 国产精品久久久久一区二区三区 | 久久久久久亚洲欧洲 | 免费视频中文字幕 | 午夜在线电影网 | 毛片在线看看 |