天天看點

優雅的微服務架構下的鑒權

從單體應用架構到分布式應用架構再到微服務架構,應用的安全通路在不斷的經受考驗。為了适應架構的變化、需求的變化,身份認證與鑒權方案也在不斷的變革。面對數十個甚至上百個微服務之間的調用,如何保證高效安全的身份認證?面對外部的服務通路,該如何提供細粒度的鑒權方案?本文将會為大家闡述微服務架構下的安全認證與鑒權方案。

一、單體應用 VS 微服務

随着微服務架構的興起,傳統的單體應用場景下的身份認證和鑒權面臨的挑戰越來越大。單體應用體系下,應用是一個整體,一般針對所有的請求都會進行權限校驗。請求一般會通過一個權限的攔截器進行權限的校驗,在登入時将使用者資訊緩存到 session 中,後續通路則從緩存中擷取使用者資訊。

優雅的微服務架構下的鑒權

而微服務架構下,一個應用會被拆分成若幹個微應用,每個微應用都需要對通路進行鑒權,每個微應用都需要明确目前通路使用者以及其權限。尤其當通路來源不隻是浏覽器,還包括其他服務的調用時,單體應用架構下的鑒權方式就不是特别合适了。在為服務架構下,要考慮外部應用接入的場景、使用者 - 服務的鑒權、服務 - 服務的鑒權等多種鑒權場景。

優雅的微服務架構下的鑒權

David Borsos 在倫敦的微服務大會上提出了四種方案:

1. 單點登入(SSO)

這種方案意味着每個面向使用者的服務都必須與認證服務互動,這會産生大量非常瑣碎的網絡流量和重複的工作,當動辄數十個微應用時,這種方案的弊端會更加明顯。

2. 分布式 Session 方案

分布式會話方案原理主要是将關于使用者認證的資訊存儲在共享存儲中,且通常由使用者會話作為 key 來實作的簡單分布式哈希映射。當使用者通路微服務時,使用者資料可以從共享存儲中擷取。在某些場景下,這種方案很不錯,使用者登入狀态是不透明的。同時也是一個高可用且可擴充的解決方案。這種方案的缺點在于共享存儲需要一定保護機制,是以需要通過安全連結來通路,這時解決方案的實作就通常具有相當高的複雜性了。

3. 用戶端 Token 方案

令牌在用戶端生成,由身份驗證服務進行簽名,并且必須包含足夠的資訊,以便可以在所有微服務中建立使用者身份。令牌會附加到每個請求上,為微服務提供使用者身份驗證,這種解決方案的安全性相對較好,但身份驗證登出是一個大問題,緩解這種情況的方法可以使用短期令牌和頻繁檢查認證服務等。對于用戶端令牌的編碼方案,Borsos 更喜歡使用 JSON Web Tokens(JWT),它足夠簡單且庫支援程度也比較好。

4. 用戶端 Token 與 API 網關結合

這個方案意味着所有請求都通過網關,進而有效地隐藏了微服務。在請求時,網關将原始使用者令牌轉換為内部會話 ID 令牌。在這種情況下,登出就不是問題,因為網關可以在登出時撤銷使用者的令牌。

二、微服務常見安全認證方案

HTTP 基本認證

HTTP Basic Authentication(HTTP 基本認證)是 HTTP 1.0 提出的一種認證機制,這個想必大家都很熟悉了,我不再贅述。HTTP 基本認證的過程如下:

  • 用戶端發送 HTTP Request 給伺服器。
  • 因為 Request 中沒有包含 Authorization header,伺服器會傳回一個 401 Unauthozied 給用戶端,并且在 Response 的 Header "WWW-Authenticate" 中添加資訊。
  • 用戶端把使用者名和密碼用 BASE64 加密後,放在 Authorization Header 中發送給伺服器, 認證成功。
  • 伺服器将 Authorization Header 中的使用者名密碼取出,進行驗證, 如果驗證通過,将根據請求,發送資源給用戶端。

基于 Session 的認證

  • 基于 Session 的認證應該是最常用的一種認證機制了。使用者登入認證成功後,将使用者相關資料存儲到 Session 中,單體應用架構中,預設 Session 會存儲在應用伺服器中,并且将 Session ID 傳回到用戶端,存儲在浏覽器的 Cookie 中。
  • 但是在分布式架構下,Session 存放于某個具體的應用伺服器中自然就無法滿足使用了,簡單的可以通過 Session 複制或者 Session 粘制的方案來解決。
  • Session 複制依賴于應用伺服器,需要應用伺服器有 Session 複制能力,不過現在大部分應用伺服器如 Tomcat、JBoss、WebSphere 等都已經提供了這個能力。
  • 除此之外,Session 複制的一大缺陷在于當節點數比較多時,大量的 Session 資料複制會占用較多網絡資源。Session 粘滞是通過負載均衡器,将統一使用者的請求都分發到固定的伺服器節點上,這樣就保證了對某一使用者而言,Session 資料始終是正确的。不過這種方案依賴于負載均衡器,并且隻能滿足水準擴充的叢集場景,無法滿足應用分割後的分布式場景。
  • 在微服務架構下,每個微服務拆分的粒度會很細,并且不隻有使用者和微服務打交道,更多還有微服務間的調用。這個時候上述兩個方案都無法滿足,就要求必須要将 Session 從應用伺服器中剝離出來,存放在外部進行集中管理。可以是資料庫,也可以是分布式緩存,如 Memchached、Redis 等。這正是 David Borsos 建議的第二種方案,分布式 Session 方案。

基于 Token 的認證

随着 Restful API、微服務的興起,基于 Token 的認證現在已經越來越普遍。Token 和 Session ID 不同,并非隻是一個 key。Token 一般會包含使用者的相關資訊,通過驗證 Token 就可以完成身份校驗。像 Twitter、微信、QQ、GitHub 等公有服務的 API 都是基于這種方式進行認證的,一些開發架構如 OpenStack、Kubernetes 内部 API 調用也是基于 Token 的認證。基于 Token 認證的一個典型流程如下:

優雅的微服務架構下的鑒權
  • 使用者輸入登入資訊(或者調用 Token 接口,傳入使用者資訊),發送到身份認證服務進行認證(身份認證服務可以和服務端在一起,也可以分離,看微服務拆分情況了)。
  • 身份驗證服務驗證登入資訊是否正确,傳回接口(一般接口中會包含使用者基礎資訊、權限範圍、有效時間等資訊),用戶端存儲接口,可以存儲在 Session 或者資料庫中。
  • 使用者将 Token 放在 HTTP 請求頭中,發起相關 API 調用。
  • 被調用的微服務,驗證 Token 權限。
  • 服務端傳回相關資源和資料。

基于 Token 認證的好處如下:

  • 服務端無狀态:Token 機制在服務端不需要存儲 session 資訊,因為 Token 自身包含了所有使用者的相關資訊。
  • 性能較好,因為在驗證 Token 時不用再去通路資料庫或者遠端服務進行權限校驗,自然可以提升不少性能。 支援移動裝置。
  • 支援跨程式調用,Cookie 是不允許垮域通路的,而 Token 則不存在這個問題。

三、JWT 介紹

簡介

JSON Web Token(JWT)是為了在網絡應用環境間傳遞聲明而執行的一種基于 JSON 的開放标準(RFC 7519)。來自 JWT RFC 7519 标準化的摘要說明:JSON Web Token 是一種緊湊的,URL 安全的方式,表示要在雙方之間傳輸的聲明。JWT 一般被用來在身份提供者和服務提供者間傳遞被認證的使用者身份資訊,以便于從資源伺服器擷取資源,也可以增加一些額外的其它業務邏輯所必須的聲明資訊,該 Token 也可直接被用于認證,也可被加密。

JWT 認證流程

  • 用戶端調用登入接口(或者擷取 token 接口),傳入使用者名密碼。
  • 服務端請求身份認證中心,确認使用者名密碼正确。
  • 服務端建立 JWT,傳回給用戶端。
  • 用戶端拿到 JWT,進行存儲(可以存儲在緩存中,也可以存儲在資料庫中,如果是浏覽器,可以存儲在 Cookie 中)在後續請求中,在 HTTP 請求頭中加上 JWT。
  • 服務端校驗 JWT,校驗通過後,傳回相關資源和資料。

JWT 結構

JWT 是由三段資訊構成的,第一段為頭部(Header),第二段為載荷(Payload),第三段為簽名(Signature)。每一段内容都是一個 JSON 對象,将每一段 JSON 對象采用 BASE64 編碼,将編碼後的内容用。連結一起就構成了 JWT 字元串。如下:

header.payload.signature
           

1. 頭部(Header)

頭部用于描述關于該 JWT 的最基本的資訊,例如其類型以及簽名所用的算法等。這也可以被表示成一個 JSON 對象。

{
"type" :  "JWT",
"ALG" :  "HS256"
}
           

在頭部指明了簽名算法是 HS256 算法。

2. 載荷(payload)

載荷就是存放有效資訊的地方。有效資訊包含三個部分:

  • 标準中注冊的聲明
  • 公共的聲明
  • 私有的聲明

标準中注冊的聲明(建議但不強制使用):

  • iss:JWT 簽發者
  • sub:JWT 所面向的使用者
  • aud:接收 JWT 的一方
  • exp:JWT 的過期時間,這個過期時間必須要大于簽發時間
  • nbf:定義在什麼時間之前,該 JWT 都是不可用的
  • iat:JWT 的簽發時間
  • jti:JWT 的唯一身份辨別,主要用來作為一次性 token, 進而回避重播攻擊。

公共的聲明 :

  • 公共的聲明可以添加任何的資訊,一般添加使用者的相關資訊或其他業務需要的必要資訊。但不建議添加敏感資訊,因為該部分在用戶端可解密。

私有的聲明 :

  • 私有聲明是提供者和消費者所共同定義的聲明,一般不建議存放敏感資訊,因為 base64 是對稱解密的,意味着該部分資訊可以歸類為明文資訊。
優雅的微服務架構下的鑒權

3. 簽名(signature)

建立簽名需要使用 Base64 編碼後的 header 和 payload 以及一個秘鑰。将 base64 加密後的 header 和 base64 加密後的 payload 使用。連接配接組成的字元串,通過 header 中聲明的加密方式進行加鹽 secret 組合加密,然後就構成了 jwt 的第三部分。

比如:

HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
           

JWT 的優點:

  • 跨語言,JSON 的格式保證了跨語言的支撐
  • 基于 Token,無狀态
  • 占用位元組小,便于傳輸

關于 Token 登出:

Token 的登出,由于 Token 不存儲在服務端,由用戶端存儲,當使用者登出時,Token 的有效時間還沒有到,還是有效的。是以如何在使用者登出登入時讓 Token 登出是一個要關注的點。一般有如下幾種方式:

  • Token 存儲在 Cookie 中,這樣用戶端登出時,自然可以清空掉
  • 登出時,将 Token 存放到分布式緩存中,每次校驗 Token 時區檢查下該 Token 是否已登出。不過這樣也就失去了快速校驗 Token 的優點。
  • 多采用短期令牌,比如令牌有效期是 20 分鐘,這樣可以一定程度上降低登出後 Token 可用性的風險。

OAuth 2.0 介紹

OAuth 的官網介紹:An open protocol to allow secure API authorization in a simple and standard method from desktop and web applications。

OAuth 是一種開放的協定,為桌面程式或者基于 BS 的 web 應用提供了一種簡單的,标準的方式去通路需要使用者授權的 API 服務。OAUTH 認證授權具有以下特點:

  • 簡單:不管是 OAuth 服務提供者還是應用開發者,都很容易于了解與使用;
  • 安全:沒有涉及到使用者密鑰等資訊,更安全更靈活;
  • 開放:任何服務提供商都可以實作 OAuth,任何軟體開發商都可以使用 OAuth;

OAuth 2.0 是 OAuth 協定的下一版本,但不向後相容 OAuth 1.0,即完全廢止了 OAuth 1.0。

OAuth 2.0 關注用戶端開發者的簡易性。要麼通過組織在資源擁有者和 HTTP 服務商之間的被準許的互動動作代表使用者,要麼允許第三方應用代表使用者獲得通路的權限。同時為 Web 應用,桌面應用和手機,和起房間裝置提供專門的認證流程。

2012 年 10 月,OAuth 2.0 協定正式釋出為 RFC 6749。

授權流程

OAuth 2.0 的流程如下:

優雅的微服務架構下的鑒權
  • (A)使用者打開用戶端以後,用戶端要求使用者給予授權。
  • (B)使用者同意給予用戶端授權。
  • (C)用戶端使用上一步獲得的授權,向認證伺服器申請令牌。
  • (D)認證伺服器對用戶端進行認證以後,确認無誤,同意發放令牌。
  • (E)用戶端使用令牌,向資源伺服器申請擷取資源。
  • (F)資源伺服器确認令牌無誤,同意向用戶端開放資源。

四大角色

由授權流程圖中可以看到 OAuth 2.0 有四個角色:用戶端、資源擁有者、資源伺服器、授權伺服器。

  • 用戶端:用戶端是代表資源所有者對資源伺服器發出通路受保護資源請求的應用程式。
  • 資源擁有者:資源擁有者是對資源具有授權能力的人。
  • 資源伺服器:資源所在的伺服器。
  • 授權伺服器:為用戶端應用程式提供不同的 Token,可以和資源伺服器在統一伺服器上,也可以獨立出去。

用戶端的授權模式

用戶端必須得到使用者的授權(Authorization Grant),才能獲得令牌(access token)。OAuth 2.0 定義了四種授權方式:authorizationcode、implicit、resource owner password credentials、client credentials。

1. 授權碼模式(authorization code)

授權碼模式(authorization code)是功能最完整、流程最嚴密的授權模式。它的特點就是通過用戶端的背景伺服器,與 "服務提供商" 的認證伺服器進行互動。流程如下:

  • 使用者通路用戶端,後者将前者導向認證伺服器。
  • 使用者選擇是否給予用戶端授權。
  • 假設使用者給予授權,認證伺服器将使用者導向用戶端事先指定的 "重定向 URI"(redirection URI),同時附上一個授權碼。
  • 用戶端收到授權碼,附上早先的 "重定向 URI",向認證伺服器申請令牌。這一步是在用戶端的背景的伺服器上完成的,對使用者不可見。
  • 認證伺服器核對了授權碼和重定向 URI,确認無誤後,向用戶端發送通路令牌(access token)和更新令牌(refresh token)。

2. 簡化模式(implicit)

簡化模式(Implicit Grant Type)不通過第三方應用程式的伺服器,直接在浏覽器中向認證伺服器申請令牌,跳過了 "授權碼" 這個步驟,是以得名。所有步驟在浏覽器中完成,令牌對通路者是可見的,且用戶端不需要認證。流程如下:

  • 用戶端将使用者導向認證伺服器。
  • 使用者決定是否給于用戶端授權。
  • 假設使用者給予授權,認證伺服器将使用者導向用戶端指定的 "重定向 URI",并在 URI 的 Hash 部分包含了通路令牌。
  • 浏覽器向資源伺服器送出請求,其中不包括上一步收到的 Hash 值。
  • 資源伺服器傳回一個網頁,其中包含的代碼可以擷取 Hash 值中的令牌。
  • 浏覽器執行上一步獲得的腳本,提取出令牌。
  • 浏覽器将令牌發給用戶端。

3. 密碼模式(Resource Owner Password Credentials)

密碼模式中,使用者向用戶端提供自己的使用者名和密碼。用戶端使用這些資訊,向 "服務商提供商" 索要授權。在這種模式中,使用者必須把自己的密碼給用戶端,但是用戶端不得儲存密碼。這通常用在使用者對用戶端高度信任的情況下,比如用戶端是作業系統的一部分,或者由一個著名公司出品。而認證伺服器隻有在其他授權模式無法執行的情況下,才能考慮使用這種模式。流程如下:

  • 使用者向用戶端提供使用者名和密碼。
  • 用戶端将使用者名和密碼發給認證伺服器,向後者請求令牌。
  • 認證伺服器确認無誤後,向用戶端提供通路令牌。

4. 用戶端模式(Client Credentials)

  • 用戶端模式(Client Credentials Grant)指用戶端以自己的名義,而不是以使用者的名義,向 "服務提供商" 進行認證。嚴格地說,用戶端模式并不屬于 OAuth 架構所要解決的問題。
  • 在這種模式中,使用者直接向用戶端注冊,用戶端以自己的名義要求 "服務提供商" 提供服務,其實不存在授權問題。流程如下:
    • 用戶端向認證伺服器進行身份認證,并要求一個通路令牌。
    • 認證伺服器确認無誤後,向用戶端提供通路令牌。

五、思考總結

  • 正如 David Borsos 所建議的一種方案,在微服務架構下,我們更傾向于将 Oauth 和 JWT 結合使用,Oauth 一般用于第三方接入的場景,管理對外的權限,是以比較适合和 API 網關結合,針對于外部的通路進行鑒權(當然,底層 Token 标準采用 JWT 也是可以的)。
  • JWT 更加輕巧,在微服務之間進行通路鑒權已然足夠,并且可以避免在流轉過程中和身份認證服務打交道。當然,從能力實作角度來說,類似于分布式 Session 在很多場景下也是完全能滿足需求
  • https://zhuanlan.zhihu.com/p/101595143

繼續閱讀