本文開始從源碼的層面,講解一些spring Security Oauth2的認(rèn)證流程。本文較長,適合在空余時間段觀看。且涉及了較多的源碼,非關(guān)鍵性代碼以…代替。
獲取token
上一篇博客中我們嘗試使用了password模式和client模式,有一個比較關(guān)鍵的endpoint:/oauth/token。從這個入口開始分析,spring security oauth2內(nèi)部是如何生成token的。
首先開啟debug信息:

可以完整的看到內(nèi)部的運(yùn)轉(zhuǎn)流程。
client模式稍微簡單一些,使用client模式獲取token
http://localhost:8080/oauth/token? client_id=client_1&client_secret=123456&scope=select&grant_type=client_credentials
由于debug信息太多了,我簡單按照順序列了一下關(guān)鍵的幾個類:

ClientCredentialsTokenEndpointFilter和DaoAuthenticationProvider
截取關(guān)鍵的代碼,可以分析出大概的流程。
在請求到達(dá)/oauth/token之前經(jīng)過了ClientCredentialsTokenEndpointFilter這個過濾器,關(guān)鍵方法如下

用來從請求中獲取clientid,clientsecret,組裝成一個UsernamePasswordAuthenticationToken作為身份標(biāo)識,使用容器中的頂級身份管理器AuthenticationManager去進(jìn)行身份認(rèn)證(AuthenticationManager的實(shí)現(xiàn)類一般是ProviderManager。而ProviderManager內(nèi)部維護(hù)了一個List,真正的身份認(rèn)證是由一系列AuthenticationProvider去完成。而AuthenticationProvider的常用實(shí)現(xiàn)類則是DaoAuthenticationProvider,DaoAuthenticationProvider內(nèi)部又聚合了一個UserDetailsService接口,UserDetailsService才是獲取用戶詳細(xì)信息的最終接口,而我們上一篇文章中在內(nèi)存中配置用戶,就是使用了UserDetailsService的一個實(shí)現(xiàn)類InMemoryUserDetailsManager)。UML類圖可以大概理解下這些類的關(guān)系,省略了授權(quán)部分。

可能機(jī)智的讀者會發(fā)現(xiàn)一個問題,我前面一片文章已經(jīng)提到了client模式是不存在“用戶”的概念的,那么這里的身份認(rèn)證是在認(rèn)證什么呢?debug可以發(fā)現(xiàn)UserDetailsService的實(shí)現(xiàn)被適配成了ClientDetailsUserDetailsService,這個設(shè)計是將client客戶端的信息(clientid,clientsecret)適配成用戶的信息(username,password),這樣我們的認(rèn)證流程就不需要修改了。
經(jīng)過ClientCredentialsTokenEndpointFilter之后,身份信息已經(jīng)得到了AuthenticationManager的驗(yàn)證。接著便到達(dá)了TokenEndpoint。
TokenEndpoint
前面的兩個ClientCredentialsTokenEndpointFilter和DaoAuthenticationProvider可以理解為一些前置校驗(yàn),和身份封裝,而這個類一看名字就知道和我們的token是密切相關(guān)的。

省略了一些校驗(yàn)代碼之后,真正的/oauth/token端點(diǎn)暴露在了我們眼前,其中方法參數(shù)中的Principal經(jīng)過之前的過濾器,已經(jīng)被填充了相關(guān)的信息,而方法的內(nèi)部則是依賴了一個TokenGranter 來頒發(fā)token。其中OAuth2AccessToken的實(shí)現(xiàn)類DefaultOAuth2AccessToken就是最終在控制臺得到的token序列化之前的原始類:

一個典型的樣例token響應(yīng),如下所示,就是上述類序列化后的結(jié)果:

TokenGranter
先從UML類圖對TokenGranter接口的設(shè)計有一個宏觀的認(rèn)識

TokenGranter的設(shè)計思路是使用CompositeTokenGranter管理一個List列表,每一種grantType對應(yīng)一個具體的真正授權(quán)者,在debug過程中可以發(fā)現(xiàn)CompositeTokenGranter 內(nèi)部就是在循環(huán)調(diào)用五種TokenGranter實(shí)現(xiàn)類的grant方法,而granter內(nèi)部則是通過grantType來區(qū)分是否是各自的授權(quán)類型。

五種類型分別是:
1.? ResourceOwnerPasswordTokenGranter ==> password密碼模式
2.? AuthorizationCodeTokenGranter ==> authorization_code授權(quán)碼模式
3.? ClientCredentialsTokenGranter ==> client_credentials客戶端模式
4.? ImplicitTokenGranter ==> implicit簡化模式
5.? RefreshTokenGranter ==>refresh_token 刷新token專用
以客戶端模式為例,思考如何產(chǎn)生token的,則需要繼續(xù)研究5種授權(quán)者的抽象類:AbstractTokenGranter

回過頭去看TokenEndpoint中,正是調(diào)用了這里的三個重要的類變量的相關(guān)方法。由于篇幅限制,不能延展太多,不然沒完沒了,所以重點(diǎn)分析下AuthorizationServerTokenServices是何方神圣。
AuthorizationServerTokenServices

在默認(rèn)的實(shí)現(xiàn)類DefaultTokenServices中,可以看到token是如何產(chǎn)生的,并且了解了框架對token進(jìn)行哪些信息的關(guān)聯(lián)。

簡單總結(jié)一下AuthorizationServerTokenServices的作用,他提供了創(chuàng)建token,刷新token,獲取token的實(shí)現(xiàn)。在創(chuàng)建token時,他會調(diào)用tokenStore對產(chǎn)生的token和相關(guān)信息存儲到對應(yīng)的實(shí)現(xiàn)類中,可以是Redis,數(shù)據(jù)庫,內(nèi)存,jwt。
總結(jié)
本篇總結(jié)了使用客戶端模式獲取Token時,spring security oauth2內(nèi)部的運(yùn)作流程,其他模式有一定的不同,但抽象功能是固定的,只是具體的實(shí)現(xiàn)類會被響應(yīng)地替換。閱讀spring的源碼,會發(fā)現(xiàn)它的設(shè)計中出現(xiàn)了非常多的抽象接口,這對我們理清楚內(nèi)部工作流程產(chǎn)生了不小的困擾,我的方式是可以借助UML類圖,先從宏觀理清楚作者的設(shè)計思路,這會讓我們的分析事半功倍。
下一篇文章重點(diǎn)分析用戶攜帶token訪問受限資源時,spring security oauth2內(nèi)部的工作流程。
原文鏈接:https://mp.weixin.qq.com/s?__biz=MzAxODcyNjEzNQ==&mid=2247484095&idx=1&sn=a4f7a5c577b17632bee715f1d87aef39&chksm=9bd0af27aca7263124d605ceada6af8b405debf941162cf3a5a63316000dce59e4686d084df4&scene=21#wechat_redirect