javascript
Spring Security 认证执行流程
本文基于 Spring Security 5.x
推薦閱讀:
項(xiàng)目集成Spring Security
SpringSecurity 整合 JWT
一、外層-正常登陸調(diào)用
項(xiàng)目啟動(dòng)后會(huì)自動(dòng)尋找 UserDetailsService 實(shí)現(xiàn)類(lèi);
執(zhí)行 UserDetailsService 的唯一方法 loadUserByName(String username) 并返回 UserDetail 類(lèi),注意,返回的 UserDetail 是根據(jù)用戶(hù)名去數(shù)據(jù)庫(kù)查詢(xún)到用戶(hù)信息;
拿到 UserDetail 后會(huì)對(duì) UserDetail 進(jìn)行一個(gè)預(yù)檢查;
預(yù)檢查啥?
用戶(hù)是否存在,是否被鎖定等等等;
全部認(rèn)證成功后會(huì)調(diào)用 AuthenticationSuccess 成功處理類(lèi),失敗則調(diào)用 AuthenticationFailHandler 類(lèi);
此時(shí)對(duì)于前后端分離項(xiàng)目而言,調(diào)用成功處理類(lèi),通常是返回由 JWT 等生成的 token json 字符串,前臺(tái)拿到返回信息后,保存 token 致本地,然后每次請(qǐng)求都會(huì)拼接到 head 中。
二、內(nèi)層-源碼級(jí)別
以訪(fǎng)問(wèn)某個(gè)項(xiàng)目中已有的鏈接為例:
http://localhost:7777/tmax/videoCategory/getAll輸入用戶(hù)名、密碼后點(diǎn)擊登錄按鈕,首先進(jìn)入 UsernamePassworkAuthenticationFilter 的父類(lèi)
AbstractAuthenticationProcessingFilter 調(diào)用 doFilter() 方法,然后再執(zhí)行 UsernamePasswordAuthenticationFilter 的 attemptAuthentication() 方法進(jìn)行驗(yàn)證;
UsernamePassworkAuthenticationFilter 類(lèi),顧名思義,表單登陸過(guò)濾器,該類(lèi)中重點(diǎn)是 attemptAuthentication() 方法:
該方法中通過(guò) 用戶(hù)名+密碼= 實(shí)例化一個(gè) UsernamePasswordAuthenticationToken 的對(duì)象,作用是將用戶(hù)請(qǐng)求的信息(用戶(hù)名、密碼、seeesion等)封裝到該對(duì)象中,我們點(diǎn)擊進(jìn)入該對(duì)象的構(gòu)造器如下圖所示:
需要說(shuō)明一點(diǎn)的是,super((Collection)null); collection 代表權(quán)限列表,在這傳了一個(gè) null 進(jìn)去是因?yàn)閯傞_(kāi)始并沒(méi)有進(jìn)行認(rèn)證,因此用戶(hù)此時(shí)沒(méi)有任何權(quán)限,并且設(shè)置沒(méi)有認(rèn)證的信息 setAuthenticated(false) ;
再回到 UsernamePassworkAuthenticationFilter attemptAuthentication() 方法,可以看到方法最后調(diào)用了 getAuthenticationManager() 方法,然后就進(jìn)入了 AuthenticationManager 接口的實(shí)現(xiàn)類(lèi) ProviderManager 中。
補(bǔ)充:AuthenticationManager 不包含驗(yàn)證用戶(hù)名以及密碼的功能,只是用來(lái)管理 AuthenticationProvider,所有的校驗(yàn)規(guī)則都是寫(xiě)在 AuthenticationProvider 中的;
繼續(xù)走,在 ProviderManager 這個(gè)實(shí)現(xiàn)類(lèi)中,它會(huì)調(diào)用AuthenticationProvider 接口的實(shí)現(xiàn)類(lèi)獲取用戶(hù)的信息,用戶(hù)的信息權(quán)限的驗(yàn)證就在該類(lèi)中校驗(yàn)。
進(jìn)入 ProviderManager 類(lèi)后會(huì)調(diào)用 authenticate(Authentication authentication) 方法,它通過(guò) AuthenticationProvider 實(shí)現(xiàn)類(lèi)獲取用戶(hù)的登錄的方式,然后會(huì)有一個(gè) while 迭代器模式的循環(huán)遍歷,檢查它是否支持這種登錄方式,具體的登錄方式有表單登錄,qq登錄,微信登錄等。如果最終都不支持會(huì)拋出相應(yīng)的異常信息,如果支持則會(huì)進(jìn)入AuthenticationProvider 接口的抽象實(shí)現(xiàn)類(lèi) AbstractUserDetailsAuthenticationProvider 中。
進(jìn)入 AbstractUserDetailsAuthenticationProvider 類(lèi)后會(huì)調(diào)用 authenticate(Authentication authentication) 方法對(duì)用戶(hù)的身份進(jìn)行校驗(yàn),首先是判斷用戶(hù)是否為空,這個(gè) user 是 UserDetail 的對(duì)象,如果為空,表示還沒(méi)有認(rèn)證,就需要調(diào)用 retrieveUser 方法去獲取用戶(hù)的信息,這個(gè)方法是抽象類(lèi) AbstractUserDetailsAuthenticationProvider 的擴(kuò)展類(lèi)DaoAuthenticationProvider 的一個(gè)方法。
在該擴(kuò)展類(lèi)的 retrieveUser 方法中調(diào)用 UserDetailsService 這個(gè)接口的實(shí)現(xiàn)類(lèi)的 loadUserByUsername 方法去獲取用戶(hù)信息,而這里我自己編寫(xiě)了實(shí)現(xiàn)類(lèi) UserDetailsServiceImpl 類(lèi),在這個(gè)實(shí)現(xiàn)類(lèi)中,我們可以編寫(xiě)自己的邏輯,從數(shù)據(jù)庫(kù)中獲取用戶(hù)密碼等權(quán)限信息返回。
本地 UserDetailService 實(shí)現(xiàn)類(lèi) UserDetailsServiceImpl:
在拿到用戶(hù)的信息后,返回到 AbstractUserDetailsAuthenticationProvider 類(lèi)中調(diào)用 createSuccessAuthentication(principalToReturn, authentication, user) 方法,在該方法中會(huì)調(diào)用三個(gè)參數(shù)的UsernamePasswordAuthenticationToken 構(gòu)造器,不同于前面調(diào)用兩個(gè)參數(shù)的,因?yàn)檫@里已經(jīng)驗(yàn)證了用戶(hù)的信息和權(quán)限,因此不再是給父類(lèi)構(gòu)造器中傳null 值了,而是用戶(hù)的權(quán)限集合,并且設(shè)置認(rèn)證通過(guò)setAuthenticated(true)
如下是 UsernamePasswordAuthenticationToken 構(gòu)造器:
此時(shí) authorities 不再為空了。
在 UsernamePasswordAuthenticationToken 的父類(lèi)中,它會(huì)檢查用的權(quán)限,如果有一個(gè)為 null,表示權(quán)限沒(méi)有相應(yīng)的權(quán)限,拋出異常。
然后在 createSuccessAuthentication 方法返回后回到 ProvioderManager 的 authenticate 方法中返回 result,最后回到UsernamePasswordAuthenticationFilter 的剛開(kāi)始進(jìn)入的 attemptAuthentication 方法中返回。
attemptAuthentication() 方法中的返回,返回到哪?
再回到第一張圖,UsernamePasswordAuthenticationFilter 父類(lèi) doFilter() 方法,返回值就是 authResult,如果過(guò)程中發(fā)現(xiàn)存在異常則執(zhí)行 unsuccessfulAuthentication.onAuthenticationFailure() 方法,如果認(rèn)證成功則執(zhí)行 successfulAuthentication.onAuthenticationSuccess() 方法,再結(jié)合上邊提到的自定義 成功/失敗處理類(lèi)。
最后總結(jié)
流程大致是,首先進(jìn)入 UsernamePasswordAuthenticationFilter 父類(lèi) AbstractAuthenticationProcessingFilter 執(zhí)行 doFilter() 方法,這個(gè) doFilter() 方法呢執(zhí)行如下:
習(xí)慣在微信看技術(shù)文章,想要獲取更多的Java資源的同學(xué),可以關(guān)注微信公眾號(hào):niceyoo
總結(jié)
以上是生活随笔為你收集整理的Spring Security 认证执行流程的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 极域电子教室
- 下一篇: SpringMVC(十七-二十) Mod