就理論而言,理論和實踐并無差異,但真付諸實行,差異即開始顯現(xiàn)。
Jan?L.A van de Snepscheut
我們在軟件開發(fā)過程中,需要針對不同的軟件產(chǎn)品需求,提供不同的身份認證方式,縱使SpringSecurity非常強大,她所提供的身份認證方式也不足以面對各式各樣的身份認證方式,所以學習如何擴展SpringSecurity的認證方式也就非常有必要了。
注意!這不是一篇對于SpringSecurity毫無了解的讀者準備的,所以在閱讀這篇文章之前你需要了解對SpringSecurity有一些基礎的了解。
“ Spring Security的認證流程
在我們自定義SpringSecurity的認證方式之前,先從SpringSecurity的用戶名/密碼認證中了解SpringSecurity的認證流程,以及參與到這個流程的類它們各自的職責是什么。
用戶名/密碼認證流程(SecurityFilterChain中每一個橙色矩形條代表一個SecurityFilter,因為這里主要講解用戶名/密碼認證,故突出顯示UsernamePasswordAuthenticationFilter)
當用戶提交他們的用戶名和密碼時,這個UsernamePasswordAuthenticationFilter會從HttpServletRequest提取用戶名和密碼,然后使用提取出的用戶名和密碼創(chuàng)建UsernamePasswordAuthenticationToken。
接下來該UsernamePasswordAuthenticationToken會作為AuthenticationManager.authentication(通常是調(diào)用AuthenticationManager的一個實現(xiàn)類ProviderManager重寫的authentication方法)方法的參數(shù)傳入。
ProviderManager從它所擁有的AuthenticationProvider的列表中找到一個支持認證UsernamePasswordAuthenticationToken的一個AuthenticationProvider(在這里是如右圖的DaoAuthenticationProvider)。
DaoAuthenticationProvider會通過UserDetailsService類使用UsernamePasswordAuthenticationToken中username去查詢用戶,然后對該用戶進行身份認證。
如果認證失?。?/p>
SecurityContextHolder被清空。
RememberMeServices.loginFail會被調(diào)用,如果rememberme沒有被配置,將不會有任何操作。
AuthenticationFailureHandler被調(diào)用。
如果認證成功:
SessionAuthenticationStrategy會收到一個登錄通知。
這個Authencation會被放置到SecurityContextHolder中,RememberMeServices.loginSuccess將被調(diào)用。
如果沒有設置rememberme,將不會有任何操作。
ApplicationEventPublisher發(fā)布一個InteractiveAuthenticationSuccessEvent。
從上述的用戶名/密碼認證的流程中,我們可以得出如下圖所示的一個通用流程。
抽象認證流程
如果認證成功:
“ 自定義身份認證
在闡述完整個認證架構后,其實不難發(fā)現(xiàn),在自定義認證的時候,你需要做如下準備:
CustomAuthenticationToken
CustomFilter
?CustomAuthenticationProvider
你需要自定義一個 AuthenticationProvider 的實現(xiàn)類和一個 Authentication 的實現(xiàn)類,還有一個繼承了AbstractAuthenticationProcessingFilter的過濾器。
對于AuthenticationProvider 的實現(xiàn)類 CustomAuthenticationProvider,需要在authenticate方法中編寫你自己的認證算法,并且通過實現(xiàn)supports方法告知調(diào)用者自己所支持認證的Authentication類型(在這里支持認證的就是CustomAuthenticationToken類型)。
CustomAuthenticationToken是一個繼承了UsernamePasswordAuthenticationToken的子類(UsernamePasswordAuthenticationToken是一個間接繼承了Authentication的類,它默認實現(xiàn)了Authentication的所有接口,大多數(shù)情況下,你只需要繼承它就可以了,?當然你也可以直接繼承Authentication),你可以從圖中看到它有兩個構造函數(shù),這兩個構造函數(shù)的使用時機是不同的,第一個構造函數(shù)是通常由CustomFilter調(diào)用,然后將創(chuàng)建的CustomAuthenticationToken傳遞給AuthenticationManager進行認證(最終會由ProviderManager委托CustomAuthenticationProvider認證),如果認證成功,CustomAuthenticationProvider會調(diào)用第二個構造函數(shù)生成一個已認證的CustomAuthenticationToken。
對于CustomFilter,它主要要做兩件事,第一,在默認構造函數(shù)中通過調(diào)用父類的setFilterProcessesUrl方法去配置需要攔截的url。第二,它需要在attemptAuthentication中,把認證相關的數(shù)據(jù)提取從HttpServletRequest中提取出來,然后調(diào)用CustomAuthenticationToken的第一個構造函數(shù)創(chuàng)建CustomAuthenticationToken,并將他傳遞給AuthenticationManager去認證。
最后,在完成了上述相關類的實現(xiàn)了之后,接下來我們只需要將它們按照下圖的代碼配置進Spring Security即可。
SpringSecurity配置
接下來,這里有幾個小測驗,可以幫助你了解你的掌握情況:
你自定義的AuthenticationProvider需要實現(xiàn)哪兩個方法,這兩個方法的用途是什么?
你自定義的AuthenticationProvider如何配置到SpringSecurity中?
你自定義的過濾器應該要繼承哪一個類?這個過濾器有哪些職責?你自定義的過濾器如何添加到SpringSecurity的過濾器鏈中?