就理論而言,理論和實(shí)踐并無(wú)差異励两,但真付諸實(shí)行,差異即開(kāi)始顯現(xiàn)囊颅。
Jan?L.A van de Snepscheut
我們?cè)谲浖_(kāi)發(fā)過(guò)程中当悔,需要針對(duì)不同的軟件產(chǎn)品需求傅瞻,提供不同的身份認(rèn)證方式,縱使SpringSecurity非常強(qiáng)大盲憎,她所提供的身份認(rèn)證方式也不足以面對(duì)各式各樣的身份認(rèn)證方式嗅骄,所以學(xué)習(xí)如何擴(kuò)展SpringSecurity的認(rèn)證方式也就非常有必要了。
注意焙畔!這不是一篇對(duì)于SpringSecurity毫無(wú)了解的讀者準(zhǔn)備的掸读,所以在閱讀這篇文章之前你需要了解對(duì)SpringSecurity有一些基礎(chǔ)的了解。
“ Spring Security的認(rèn)證流程
在我們自定義SpringSecurity的認(rèn)證方式之前宏多,先從SpringSecurity的用戶(hù)名/密碼認(rèn)證中了解SpringSecurity的認(rèn)證流程儿惫,以及參與到這個(gè)流程的類(lèi)它們各自的職責(zé)是什么。
用戶(hù)名/密碼認(rèn)證流程(SecurityFilterChain中每一個(gè)橙色矩形條代表一個(gè)SecurityFilter伸但,因?yàn)檫@里主要講解用戶(hù)名/密碼認(rèn)證肾请,故突出顯示UsernamePasswordAuthenticationFilter)
當(dāng)用戶(hù)提交他們的用戶(hù)名和密碼時(shí),這個(gè)UsernamePasswordAuthenticationFilter會(huì)從HttpServletRequest提取用戶(hù)名和密碼更胖,然后使用提取出的用戶(hù)名和密碼創(chuàng)建UsernamePasswordAuthenticationToken铛铁。
接下來(lái)該UsernamePasswordAuthenticationToken會(huì)作為AuthenticationManager.authentication(通常是調(diào)用AuthenticationManager的一個(gè)實(shí)現(xiàn)類(lèi)ProviderManager重寫(xiě)的authentication方法)方法的參數(shù)傳入。
ProviderManager從它所擁有的AuthenticationProvider的列表中找到一個(gè)支持認(rèn)證UsernamePasswordAuthenticationToken的一個(gè)AuthenticationProvider(在這里是如右圖的DaoAuthenticationProvider)却妨。
DaoAuthenticationProvider會(huì)通過(guò)UserDetailsService類(lèi)使用UsernamePasswordAuthenticationToken中username去查詢(xún)用戶(hù)饵逐,然后對(duì)該用戶(hù)進(jìn)行身份認(rèn)證。
如果認(rèn)證失敱氡辍:
SecurityContextHolder被清空倍权。
RememberMeServices.loginFail會(huì)被調(diào)用,如果rememberme沒(méi)有被配置捞烟,將不會(huì)有任何操作薄声。
AuthenticationFailureHandler被調(diào)用。
如果認(rèn)證成功:
SessionAuthenticationStrategy會(huì)收到一個(gè)登錄通知题画。
這個(gè)Authencation會(huì)被放置到SecurityContextHolder中默辨,RememberMeServices.loginSuccess將被調(diào)用。
如果沒(méi)有設(shè)置rememberme苍息,將不會(huì)有任何操作缩幸。
ApplicationEventPublisher發(fā)布一個(gè)InteractiveAuthenticationSuccessEvent。
從上述的用戶(hù)名/密碼認(rèn)證的流程中竞思,我們可以得出如下圖所示的一個(gè)通用流程桌粉。
抽象認(rèn)證流程
如果認(rèn)證成功:
“ 自定義身份認(rèn)證
在闡述完整個(gè)認(rèn)證架構(gòu)后桑腮,其實(shí)不難發(fā)現(xiàn),在自定義認(rèn)證的時(shí)候蛉幸,你需要做如下準(zhǔn)備:
CustomAuthenticationToken
CustomFilter
?CustomAuthenticationProvider
你需要自定義一個(gè) AuthenticationProvider 的實(shí)現(xiàn)類(lèi)和一個(gè) Authentication 的實(shí)現(xiàn)類(lèi)破讨,還有一個(gè)繼承了AbstractAuthenticationProcessingFilter的過(guò)濾器。
對(duì)于AuthenticationProvider 的實(shí)現(xiàn)類(lèi) CustomAuthenticationProvider奕纫,需要在authenticate方法中編寫(xiě)你自己的認(rèn)證算法提陶,并且通過(guò)實(shí)現(xiàn)supports方法告知調(diào)用者自己所支持認(rèn)證的Authentication類(lèi)型(在這里支持認(rèn)證的就是CustomAuthenticationToken類(lèi)型)。
CustomAuthenticationToken是一個(gè)繼承了UsernamePasswordAuthenticationToken的子類(lèi)(UsernamePasswordAuthenticationToken是一個(gè)間接繼承了Authentication的類(lèi)匹层,它默認(rèn)實(shí)現(xiàn)了Authentication的所有接口隙笆,大多數(shù)情況下,你只需要繼承它就可以了又固,?當(dāng)然你也可以直接繼承Authentication),你可以從圖中看到它有兩個(gè)構(gòu)造函數(shù)煤率,這兩個(gè)構(gòu)造函數(shù)的使用時(shí)機(jī)是不同的仰冠,第一個(gè)構(gòu)造函數(shù)是通常由CustomFilter調(diào)用,然后將創(chuàng)建的CustomAuthenticationToken傳遞給AuthenticationManager進(jìn)行認(rèn)證(最終會(huì)由ProviderManager委托CustomAuthenticationProvider認(rèn)證)蝶糯,如果認(rèn)證成功洋只,CustomAuthenticationProvider會(huì)調(diào)用第二個(gè)構(gòu)造函數(shù)生成一個(gè)已認(rèn)證的CustomAuthenticationToken。
對(duì)于CustomFilter昼捍,它主要要做兩件事识虚,第一,在默認(rèn)構(gòu)造函數(shù)中通過(guò)調(diào)用父類(lèi)的setFilterProcessesUrl方法去配置需要攔截的url妒茬。第二担锤,它需要在attemptAuthentication中,把認(rèn)證相關(guān)的數(shù)據(jù)提取從HttpServletRequest中提取出來(lái)乍钻,然后調(diào)用CustomAuthenticationToken的第一個(gè)構(gòu)造函數(shù)創(chuàng)建CustomAuthenticationToken肛循,并將他傳遞給AuthenticationManager去認(rèn)證铭腕。
最后,在完成了上述相關(guān)類(lèi)的實(shí)現(xiàn)了之后多糠,接下來(lái)我們只需要將它們按照下圖的代碼配置進(jìn)Spring Security即可累舷。
SpringSecurity配置
接下來(lái),這里有幾個(gè)小測(cè)驗(yàn)夹孔,可以幫助你了解你的掌握情況:
你自定義的AuthenticationProvider需要實(shí)現(xiàn)哪兩個(gè)方法被盈,這兩個(gè)方法的用途是什么?
你自定義的AuthenticationProvider如何配置到SpringSecurity中搭伤?
你自定義的過(guò)濾器應(yīng)該要繼承哪一個(gè)類(lèi)只怎?這個(gè)過(guò)濾器有哪些職責(zé)?你自定義的過(guò)濾器如何添加到SpringSecurity的過(guò)濾器鏈中闷畸?