微服務系列之授權認證(一) OAuth 2.0 和 OpenID Connect
1.傳統架構的授權認證
傳統應用架構,用戶使用帳號密碼登錄後,可以使用前端cookie存儲登錄狀態,也可以使用後端session方式存儲登錄狀態,小應用這麼做其實很高效實用,當應用需要橫向擴展時,就需要共享登錄狀態,這時候session的基於asp.net state這種當前伺服器進程方式存儲就失效了,需要換成sqlserver或者redis作為session共享存儲,這些都不是問題,這些前提都是單體應用架構的方案,但是在微服務架構里,服務拆分零散且前後端分離,後端以API方式提供服務的前提下,這種認證授權方式用不了拉,這時候就需要一個安全的、跨分散式的、高性能的認證授權方案來解決。
這些年圍繞著授權鑒權(authorization)和身份驗證(authentication)誕生了很多規範和協議。這裡只討論最主流的最新的規範和協議:OAuth2.0、OpenID Connect、JWT。8
2.OAuth 2.0
OAuth 2.0是關於授權鑒權的,一句話解釋「OAuth 2.0是一種框架,其中服務的用戶可以允許第三方應用程式訪問他/她在服務中託管的數據,而無需嚮應用程式透露他/她的憑據」。
說一下Oauth2.0相關的名詞:
- Resource Owner:資源所有者,就是某個應用的用戶;
- Client:客戶端,一個想要用這個資源用戶的名義去做一些事情的應用」;
- Authorization Server:授權服務,前提是用戶信任這個服務並且該服務擁有用戶資訊,用於頒發令牌給應用;
- Resource Server: 一個應用(API或者服務);
- Redirect URI: 一個網址URL,當Resource Owner在Authorization Server上授權了Client後,Authorization Server將會把Resource Owner重定向到的地方,也稱「Callback URL」;
- Authorization Code: 用戶授權給client後通過RedirectUrl回調回去攜帶的code,用於client通過客戶端模式向授權服務換取token;
- Access Token: Client和Resource Server交互所使用的令牌,攜帶的訪問許可權範圍,是你授權時通過勾選給client的,然後client拿著這個用戶名義的token就可以訪問你的資源了; 一般使用的是JWT格式;
- Response Type: Client希望從Authorization Server收到的資訊的類型,最常見的Response Type是code,也就是Client希望收到一個Authorization Code,也有Implicit隱藏式,password密碼模式,Client Credential客戶端憑證模式。
下面說一下幾種常用授權類型的實際交互是怎麼樣的
(1) 授權碼模式Authorization Code
(2)Implicit隱藏式
這種模式,跳過獲取code的步驟,在客戶端重定向到授權服務時,講responseType換成token。
(3)password密碼模式
這種是啥呢,需要用戶非常非常信任client的時候,才使用這種模式,需要用戶在clinet上輸入帳號密碼,client拿著用戶的帳號密碼去授權服務獲取access token。
(4)Client Credential客戶端憑證模式
這是client與client之間的通訊,與用戶沒啥關係,這種用於,流程是,A客戶端使用clientId和secret通過授權服務,獲取訪問B客戶端的token。我們實戰中,是業務系統,在中台認證中心裡獲取一個長期的可以訪問中台某些服務的token,業務服務直接通訊中台服務,於用戶無關。
3.OpenID Connect
OpenID Connect實際上就是對於client來說,在OAuth 2協議上完善了身份認證的東西,並不是說Oauth 2.0沒有提供認證能力,只是對於client來說,沒有知道用戶的認證過程,沒有拿到用戶認證資訊而已,OIDC就是讓OAuth把認證結果也告訴client,讓client也知道了用戶是認證過的。這樣在授權碼過程中如下圖:
HTTP/1.1 302 Found Location: https://server.example.com/authorize? response_type=code &scope=openid%20profile%20email &client_id=s6BhdRkqt3 &state=af0dasd &redirect_uri=https%3A%2F%2Fclient.example.org%2Fcb
在scope中增加了openid
HTTP/1.1 200 OK Content-Type: application/json Cache-Control: no-store Pragma: no-cache { "access_token": "dasdqwdqd", "token_type": "Bearer", "refresh_token": "casdqwfw", "expires_in": 3600, "id_token": "dsadwqdqdqwdqV4YW1wbGUuY29tIiwKICJzdWIiOiAiMjQ4Mjg5 NzYxMDAxIiwKICJhdWQiOiAiczZCaGRSa3F0MyIsCiAibm9uY2UiOiAibi0wUzZ fV3pBMk1qIiwKICJleHAiOiAxMzExMjgxOTcwLAogImlhdCI6IDEzMTEyODA5Nz AKfQ.ggW8hZ1EuVLuxNuuIJKX_V8a_OMXzR0EHR9R6jgdqrOOF4daGU96Sr_P6q Jp6IcmD3HP99Obi1PRs-cwh3LO-p146waJ8IhehcwL7F09JdijmBqkvPeB2T9CJ NqeGpe-gccMg4vfKjkM8FcGvnzZUN4_KSP0aAp1tOJ1zZwgjxqGByKHiOtX7Tpd QyHE5lcMiKPXfEIQILVq0pcgqeqgeqwhethrDSAdqwwqrt43t3" }
在返回的時候增加了id_token。
那麼問題來了,OAouth2.0 給客戶端辦法的access token中的Payload中是可以自定義的,並且也可以防篡改,直接把認證的身份資訊自定義里多好啊。?或者說,通過access_token去oidc提供的一個endpoint(get/userinfo)去請求用戶資訊?
原因以下幾點:
1)payload里可是明文傳輸的,增加了傳輸頻寬,也增加了用戶資訊泄露的風險;
2)access_token本身定義就是授權訪問令牌,不關心用戶資訊,只關心是否能訪問,功能耦合;
3)通過endpoint請求用戶資訊,增加了不少額外的API開銷。
下一節,我們會大至說一下.net core服務中基於OAuth2.0和OpenId Connect實現的框架 identiy server 4