本文基於 Spring Security 5.xhtml
項目啓動後會自動尋找 UserDetailsService 實現類;後端
執行 UserDetailsService 的惟一方法 loadUserByName(String username) 並返回 UserDetail 類,注意,返回的 UserDetail 是根據用戶名去數據庫查詢到用戶信息;ruby
拿到 UserDetail 後會對 UserDetail 進行一個預檢查;微信
預檢查啥?session
用戶是否存在,是否被鎖定等等等;app
所有認證成功後會調用 AuthenticationSuccess 成功處理類,失敗則調用 AuthenticationFailHandler 類;前後端分離
此時對於先後端分離項目而言,調用成功處理類,一般是返回由 JWT 等生成的 token json 字符串,前臺拿到返回信息後,保存 token 致本地,而後每次請求都會拼接到 head 中。ide
以訪問某個項目中已有的連接爲例:
http://localhost:7777/tmax/videoCategory/getAll
輸入用戶名、密碼後點擊登陸按鈕,首先進入 UsernamePassworkAuthenticationFilter 的父類
AbstractAuthenticationProcessingFilter 調用 doFilter() 方法,而後再執行 UsernamePasswordAuthenticationFilter 的 attemptAuthentication() 方法進行驗證;
UsernamePassworkAuthenticationFilter 類,顧名思義,表單登錄過濾器,該類中重點是 attemptAuthentication() 方法:
該方法中經過 用戶名+密碼= 實例化一個 UsernamePasswordAuthenticationToken 的對象,做用是將用戶請求的信息(用戶名、密碼、seeesion等)封裝到該對象中,咱們點擊進入該對象的構造器以下圖所示:
須要說明一點的是,super((Collection)null); collection 表明權限列表,在這傳了一個 null 進去是由於剛開始並無進行認證,所以用戶此時沒有任何權限,而且設置沒有認證的信息 setAuthenticated(false) ;
再回到 UsernamePassworkAuthenticationFilter attemptAuthentication() 方法,能夠看到方法最後調用了 getAuthenticationManager() 方法,而後就進入了 AuthenticationManager 接口的實現類 ProviderManager 中。
補充:AuthenticationManager 不包含驗證用戶名以及密碼的功能,只是用來管理 AuthenticationProvider,全部的校驗規則都是寫在 AuthenticationProvider 中的;
繼續走,在 ProviderManager 這個實現類中,它會調用AuthenticationProvider 接口的實現類獲取用戶的信息,用戶的信息權限的驗證就在該類中校驗。
進入 ProviderManager 類後會調用 authenticate(Authentication authentication) 方法,它經過 AuthenticationProvider 實現類獲取用戶的登陸的方式,而後會有一個 while 迭代器模式的循環遍歷,檢查它是否支持這種登陸方式,具體的登陸方式有表單登陸,qq登陸,微信登陸等。若是最終都不支持會拋出相應的異常信息,若是支持則會進入AuthenticationProvider 接口的抽象實現類 AbstractUserDetailsAuthenticationProvider 中。
進入 AbstractUserDetailsAuthenticationProvider 類後會調用 authenticate(Authentication authentication) 方法對用戶的身份進行校驗,首先是判斷用戶是否爲空,這個 user 是 UserDetail 的對象,若是爲空,表示尚未認證,就須要調用 retrieveUser 方法去獲取用戶的信息,這個方法是抽象類 AbstractUserDetailsAuthenticationProvider 的擴展類DaoAuthenticationProvider 的一個方法。
在該擴展類的 retrieveUser 方法中調用 UserDetailsService 這個接口的實現類的 loadUserByUsername 方法去獲取用戶信息,而這裏我本身編寫了實現類 UserDetailsServiceImpl 類,在這個實現類中,咱們能夠編寫本身的邏輯,從數據庫中獲取用戶密碼等權限信息返回。
本地 UserDetailService 實現類 UserDetailsServiceImpl:
在拿到用戶的信息後,返回到 AbstractUserDetailsAuthenticationProvider 類中調用 createSuccessAuthentication(principalToReturn, authentication, user) 方法,在該方法中會調用三個參數的UsernamePasswordAuthenticationToken 構造器,不一樣於前面調用兩個參數的,由於這裏已經驗證了用戶的信息和權限,所以再也不是給父類構造器中傳null 值了,而是用戶的權限集合,而且設置認證經過setAuthenticated(true)
以下是 UsernamePasswordAuthenticationToken 構造器:
此時 authorities 再也不爲空了。
在 UsernamePasswordAuthenticationToken 的父類中,它會檢查用的權限,若是有一個爲 null,表示權限沒有相應的權限,拋出異常。
而後在 createSuccessAuthentication 方法返回後回到 ProvioderManager 的 authenticate 方法中返回 result,最後回到UsernamePasswordAuthenticationFilter 的剛開始進入的 attemptAuthentication 方法中返回。
attemptAuthentication() 方法中的返回,返回到哪?
再回到第一張圖,UsernamePasswordAuthenticationFilter 父類 doFilter() 方法,返回值就是 authResult,若是過程當中發現存在異常則執行 unsuccessfulAuthentication.onAuthenticationFailure() 方法,若是認證成功則執行 successfulAuthentication.onAuthenticationSuccess() 方法,再結合上邊提到的自定義 成功/失敗處理類。
流程大體是,首先進入 UsernamePasswordAuthenticationFilter 父類 AbstractAuthenticationProcessingFilter 執行 doFilter() 方法,這個 doFilter() 方法呢執行以下:
習慣在微信看技術文章,想要獲取更多的Java資源的同窗,能夠關注微信公衆號:niceyoo