微服務 - API 網關 - 身份驗證

一、身份驗證介紹

身份驗證通常是進入系統的第一道大閘,要求用戶出具登錄此係統的身份證明。其實在實際情況下,很多人開發的系統都沒有身份驗證功能或者就只有普通用戶名和密碼驗證功能,這樣的系統其實都是不完善的,也是不符合安全標準的,所以作爲這麼大衆化的系統,我們有必要做詳細的學習和了解。

身份驗證分類

對於身份驗證,我們分兩種情況來看待,界面登錄驗證和接口調用驗證,這兩種方式所使用的身份驗證方法很多時候是不一樣的。

1、界面登錄驗證

指用戶通過前端(包含瀏覽器端和移動端)來進入系統,用戶一旦登錄成功後,用戶後續的操作便是以此身份來執行,常規的做法是服務端生成相應的 Session,前端生成 Cookie。常見的界面登錄解決方案有 SSO、Ldap、OAuth、Basic、Kerberos 等。下面我們說說這幾個解決方案。

1.1、Basic

普通用戶名密碼登錄,通常把用戶名和密碼以明文或者加密後的方式存儲在數據庫中,當用戶在前端輸入用戶名密碼後,後端進行驗證,通過後會生成相應的 Session 信息,這樣就算驗證成功了。

這是最基本的一種身份驗證方式,在開源軟件的身份驗證功能裏面通常都會帶有這種方式。

1.2、Ldap

LDAP(Light Directory Access Portocol),它是基於 X.500 標準的輕量級目錄訪問協議。

目錄數據庫是一個爲查詢、瀏覽和搜索而優化的數據庫,它成樹狀結構組織數據,類似文件目錄一樣。目錄數據庫和關係數據庫不同,它有優異的讀性能,但寫性能差,並且沒有事務處理、回滾等複雜功能,不適於存儲修改頻繁的數據。所以目錄天生是用來查詢的,就好象它的名字一樣。

LDAP 目錄服務是由目錄數據庫和一套訪問協議組成的系統。

1.3、OAuth

OAuth 2.0 是一個行業的標準授權協議。OAuth 2.0 專注於簡化客戶端開發人員,同時爲 Web 應用程序,桌面應用程序,手機和客廳設備提供特定的授權流程。

它的最終目的是爲第三方應用頒發一個有時效性的令牌 token。使得第三方應用能夠通過該令牌獲取相關的資源。常見的場景就是:第三方登錄。當你想要登錄某個論壇,但沒有賬號,而這個論壇接入瞭如 QQ、Facebook 等登錄功能,在你使用 QQ 登錄的過程中就使用的 OAuth 2.0 協議。

1.4、Kerberos

Kerberos 是一個網絡認證的框架協議,其設計的初衷便是通過密鑰系統爲 Client 和 Server 應用程序之間提供強大的認證服務。在使用 Kerberos 認證的集羣中,Client 不會直接和 Server 進行認證,而是通過 KDC(Key Distribution Center)來完成互相的認證。

Kerberos 是一種單點登錄解決方案,使用場景之一是在大數據(Hadoop)集羣中,用來作爲大數據安全的基礎保障。

1.5、SSO

SSO 英文全稱是 Single Sign On,即單點登錄。在多個系統中,用戶只需要在其中一個系統中登錄過了,就不需要在其他系統中再登錄了。

SSO 登錄方式也是現在最流行的驗證方式,在公司內部會有很多套系統,什麼 Wiki、Gitlab、Jira 等等,如果大家在進入每個系統前都去輸入用戶名密碼搞一次,這是挺煩人的,同時也浪費了很多時間和精力,如果用 SSO 只需要登錄一次就好了,豈不是大快人心。

SSO 代表一種概念,並不是一種具體的解決方案,最常見的解決方案是 CAS,這個我們在後面的章節會單獨講解。CAS 可以和 Ldap 一起構成完整的登錄解決方案。

2、接口調用驗證

指用戶直接通過調用系統的 RESTful API 等方式來發起操作,常見的接口調用驗證方案有 HMAC、JWT、證書等。

2.1、HMAC

HMAC 英文全稱是 Hash-based Message Authentication Code,即基於 Hash 的消息的認證碼。

我特地在網上搜索了 HMAC 的原理,但絕大部分要麼解釋的不清不楚,自己也搞不清楚在說什麼,要麼就是曲解了真正的意思。我這裏用盡量通俗的話來讓大家明白。

如果你調用過一些雲廠商(比如騰訊雲、阿里雲、七牛雲)的公共接口的話,你可能會有印象,他們會給你兩個 Key,一個叫 AccessKey,另一個叫 SecretKey,有的廠商會用 UserName 替代 AccessKey,其實是一個意思,UserName 或者 AccessKey 都表示某個特定的用戶,而 SecretKey 表示這是要保密的,不能被別人知道的,在請求數據被傳輸的時候是不會包含 SecretKey 的。

一般把請求的參數放在一起作爲一個字符串,然後用 SecretKey 做密鑰,採用一種哈希算法(有 HMAC-MD5、HMAC-SHA256、HMAC-SHA512 等)把這個字符串做哈希加密,然後把加密後的字符串作爲一個參數,並把 AccessKey 也作爲一個參數,連同原始請求數據一起傳遞給接受者。

接受者根據 AccessKey 知道了是那個用戶發送的請求,查詢出對應的 SecretKey,然後拿着這個 SecretKey 把傳遞過來的數據按照客戶端的方式在加密一次,比對加密後的字符串是否一致,如果一致表示數據沒有被篡改,最後返回結果。

HMAC 的設計是用來防止請求數據在傳輸途中被人篡改,同時也可以防止密鑰泄漏,它並不能防止數據被別人截獲後看到原始數據,要防止數據被人截獲後看到原始數據,就需要 Https、SSL 這樣的傳輸加密措施。

2.2、JWT

JWT 全稱是 JSON Web Token,也是比較常見的身份驗證方式,這種方式服務端部保存 Session 信息,所有用戶信息都在客戶端。下面簡單說下 JWT 的流程。

用戶在客戶端把帶有用戶名和密碼的請求發送給服務端後,服務端生成一串字符串(Token),還有一個密鑰,然後把 Token 返回給客戶端。

用戶接受到 Token,字符串由三段字符串組成:Header.Payload.Signature,Header 裏面包含簽名算法(比如 HMAC-SHA256),Payload 包含過期時間、簽發時間、編號等信息,Signature 是對 Header 和 Payload 的簽名加密。

用戶隨後的請求都會帶着這個 Token,這樣服務端就知道這個用戶是誰了。

細細思考下,就會發現一旦用戶發送的信息被別人截獲,那麼壞人就可以隨意的向服務端發送請求了。

再細細的想象,由 JWT 的實現思路,我們可以簡化下流程,Token 信息其實不需要那麼多,只要一串字符串就可以了,服務端保存這個 Token,用戶每次發送請求都帶着這個 Token,這樣也能達到一樣的效果。

二、界面登錄身份驗證如何設計

設計原理

相信有些用過 MVC 框架的同學對接過 SSO 登錄系統,比如在 Django 中對接 CAS 單點登錄系統。我們要說的架構設計和 MVC 框架不太一樣,因爲 MVC 框架是前後端一體的,而微服務中的架構都是前後端分離的。

MVC 框架

請求先到達後端服務接口,以此進行身份驗證、錯誤處理、權限控制、動作執行等過程,然後把結果填充到 HTML/JS 模塊,最後把 HTML/JS 內容返回給用戶瀏覽器。

微服務框架

前端服務和後端服務都有獨立的服務地址(IP + 端口),請求先到達前端服務,然後前端服務再去後端服務請求需要渲染的元素和內容。

身份驗證作爲 API 網關裏面的一個組件,可以以模塊的方式運行,也可以以微服務的方式運行,這裏我們推薦以微服務的方式運行。它包含了界面登錄模塊和接口調用驗證模塊。我們先說界面登錄模塊的架構設計。

架構圖

流程:

  1. 用戶在瀏覽器訪問前端服務後,前端服務會把請求轉發到 API 網關。

  2. 網關把請求轉發到身份驗證服務,驗證服務查看請求頭中的 Token 信息是否有效,發現無效,請求被重定向到 CAS Server。

  3. 用戶在正確輸入用戶名密碼後,CAS Server 返回令牌並把請求重定向到前端服務。

  4. 前端服務把請求發送給 API 網關,網關再發送給身份驗證服務,驗證服務確認令牌有效後生成 Session 信息,並把生成的 Token 信息返回給前端服務。

  5. 前端服務隨後的請求的請求頭中都會帶着 Token 信息直到瀏覽器窗口關閉,身份驗證服務校驗 Token 信息成功,然後把請求轉發給後端服務,處理完後返回結果。

關鍵點設計

1、跨域訪問

API 網關對接的服務和網關本身的服務通常是不在一個域名下面的,比如工單系統前端服務的域名是 frontend.gongdan.company.com,網關裏面的身份驗證服務的域名是 authentication.apigw.company.com,前端服務每次請求中帶有的 Token 信息能不能放在 Cookie 中?

答案是不能的,因爲 Cookie 中的信息是無法跨域訪問的,我們可以把 Token 信息放在請求頭中或者 POST Body 中。

爲了實現跨域訪問,網關的接受請求模塊的響應頭中還需要設置 Access-Control-Allow-Origin=*,這樣用戶請求才能被正常處理。

2、緩存

如果前端服務的每次請求都會被網關轉發到身份驗證服務去校驗 Session 信息,那麼這個時間週期就比較長,爲了提高性能,我們可以在 API 網關裏面設計一個緩存,用於存放已通過驗證用戶的 Session 信息,這樣請求時間週期就短了。

有人可能要問了,緩存裏面的 Session 信息如何更新呢?

如果請求裏面的 Token 信息在緩存中查詢不到的話,網關再去身份驗證服務拉取一份最新的 Session 信息。

3、CAS Client

界面登錄模塊中實現了 CAS Client,其中包括把用戶的請求重定向到 CAS Server,和 CAS Server 的令牌認證以及退出操作。

三、接口調用身份驗證如何設計

這裏介紹一種最常用的設計方案。

Token

爲用戶生成一個單獨的 Token,即一長串字符串,Token 和用戶是一對一的關係,每個 Token 還對應多個微服務裏面的多個接口,Token 設置一定的過期時間,過期後自動更新。如下圖所示,

上面的設計只是一種參考方案,如果你的需求和這個設計不匹配,你可以自行改動設計。

四、爲什麼需要 SSO 登錄方式

隨着新的業務網站不斷的增加,用戶在每個應用系統中都有獨立的賬號,這樣就造成在訪問不同的應用系統時,需要記錄對應的用戶名和密碼,多個用戶名密碼極易記混,如果忘記或記錯了某一個業務網站的用戶名或密碼就無法進行登錄,耽誤工作,影響工作效率,隨着局內信息化進程的推進還會有新的應用系統產生,如果不引入單一用戶登錄的解決方案,全公司工作人名特別是承擔審批權限的各級領導很難記清各類應用系統的用戶名和密碼,嚴重影響由信息化帶來快捷性和高效性。

因爲後面的章節會圍繞 SSO 來詳細說明應該如何設計 SSO 登錄解決方案,所以下面我先給大家說說 SSO 登錄的原理。我們就拿使用最廣泛的 CAS 解決方案來說吧。

SSO 之 CAS 介紹

CAS 是 SSO 的一種具體解決方案,也是其中最流行的一種。CAS(Central Authentication Service)是耶魯大學的一個開源項目,旨在爲 web 應用系統提供一種可靠的單點登錄解決方案。

CAS 有 CAS Client 和 CAS Server 兩個組件構成,CAS Server 就是中心認證服務端,用戶的訪問只要通過它的認證就可以不用登錄直接在訪問其他系統了。

CAS 和 Ldap 的區別:

CAS 中的關鍵術語

用戶登錄流程

用戶首次訪問系統 A:

  1. 用戶瀏覽器訪問系統 A 中受限資源,此時系統 A 進行登錄檢查,發現未登錄,然後系統 A 檢查訪問的 URL 中是否帶有令牌(ST),發現沒有令牌(因爲是首次登錄)。

  2. 系統 A 將用戶請求重定向到認證中心(CAS Server),認證中心進行獲取全局票據(TGT)操作,沒有(因爲是首次登錄),所以用戶需要進行登錄操作(輸入用戶名和密碼)。

  3. 認證中心呈現登錄頁面,用戶登錄,登錄成功後,認證中心給用戶瀏覽器設置 Cookie(TGC),然後認證中心重定向請求到系統 A,並附上認證通過令牌(ST),此時認證中心同時生成了全局票據(TGT)。

  4. 此時再次進行登錄檢查,發現未登錄,然後再次獲取令牌操作,此時可以獲得令牌(ST),系統 A 拿着令牌和請求 url 與認證中心通信,驗證令牌有效,認證中心返回成功,證明用戶已登錄。

  5. 系統 A 生成相應的 session(用於下次用戶訪問時驗證),然後將受限資源返給用戶。

已登錄用戶首次訪問系統 B:

  1. 瀏覽器訪問另一系統 B 需登錄受限資源,此時進行登錄檢查(session 檢查),發現未登錄,然後系統 B 檢查訪問的 URL 中是否帶有令牌(ST),發現沒有令牌。

  2. 系統 B 將請求重定向到認證中心,認證中心進行獲取全局票據(TGT)操作,可以獲得(因爲之前已經登錄過)。

  3. 認證中心發放令牌(ST),並攜帶該令牌重定向到系統 B。

  4. 系統 B 再次進行登錄檢查,發現未登錄,然後再次獲取令牌操作,此時可以獲得令牌(ST),系統 B 拿着令牌和請求 url 與認證中心通信,驗證令牌有效,認證中心返回成功,證明用戶已登錄。

  5. 系統 B 生成相應的 session(用於下次用戶訪問時驗證),然後將受限資源返回給用戶。

用戶登出:

  1. 用戶向系統 A 發起登出請求動作,系統 A 清除本地 session,同時清除用戶瀏覽器中相應的 cookie。

  2. 系統 A 將用戶請求重定向到認證中心的登出接口,認證中心根據獲取到的票據(TGT)清除本地 session,同時清除用戶瀏覽器中相應的 cookie(TGC)。

  3. 認證中心把用戶請求重定向回系統 A,這時由於系統 A 中已經沒有了 session,所以請求會被重定向到認證中心的登錄頁面。至此用戶已經完全的退出系統了。

說明:

        由於不同的開發框架很多都沒有自帶的 CAS 登錄模塊,而且它們在登錄模塊實現上都不太一樣,所以大家對不同的開發框架都需要自己去實現相應的 CAS 登錄模塊,比如 Django 框架和 Tornado 框架就需要對它們分別實現。這一點比較麻煩,後面我會說到如何在微服務架構中做一個通用的統一登錄系統,這樣就可以一勞永逸了。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/-MzVRnOwj59p5EWjWc3wtQ