<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          聊聊微服務架構(gòu)中的認證鑒權(quán)那些事

          共 7251字,需瀏覽 15分鐘

           ·

          2021-11-24 09:05

          點擊上方“服務端思維”,選擇“設為星標

          回復”669“獲取獨家整理的精選資料集

          回復”加群“加入全國服務端高端社群「后端圈」


          作者 | 董澤潤
          出品?| 董澤潤的技術(shù)筆記

          上半年參與的項目涉及到 gateway 和 id 權(quán)限認證系統(tǒng),通過系統(tǒng)性的學習與接觸,了解很多 HTTP 鑒權(quán)的那些事。分享實踐的細節(jié),都是通用做法,符合標準協(xié)議,不涉及公司機密

          本文主要講如何給第三方服務,即 API 做鑒權(quán),而不是用戶登錄系統(tǒng)。一般做后端微服務的很少接觸這方面的概念,網(wǎng)關(guān)層或是入口做好認證后,鏈路下游都是默認開放的,最多用 iptables 或 aws securit group 類似的做網(wǎng)絡可達的限制。用戶信息傳到 Context 中即可

          如果業(yè)務需要與第三方公司 partner 合作,或是開放 API 給外部,那么需要全面了解 HTTP 鑒權(quán)的知識。本文參考了鳳凰架構(gòu)[1]HTTP API 認證授權(quán)術(shù)[2]

          基本概念

          鑒權(quán)的本質(zhì):用戶 (user / service) 是否有以及如何獲得權(quán)限 (Authority) 去操作 (Operate) 哪些資源 (Resource)

          認證(Authentication):系統(tǒng)如何正確分辨出操作用戶的真實身份?認證方式有很多種,后面會講解

          授權(quán)(Authorization):系統(tǒng)如何控制一個用戶該看到哪些數(shù)據(jù)、能操作哪些功能?授權(quán)與認證是硬幣的兩面

          憑證(Credential):系統(tǒng)如何保證它與用戶之間的承諾是雙方當時真實意圖的體現(xiàn),是準確、完整且不可抵賴的?我們一般把憑證代稱為 TOKEN

          保密(Confidentiality):系統(tǒng)如何保證敏感數(shù)據(jù)無法被包括系統(tǒng)管理員在內(nèi)的內(nèi)外部人員所竊取、濫用?這里要求我們不能存儲明文到 DB 中,不能將密碼寫到 http url 中,同時要求 id 服務僅有少部分人能夠訪問,并且有審計

          傳輸(Transport Security):系統(tǒng)如何保證通過網(wǎng)絡傳輸?shù)男畔o法被第三方竊聽、篡改和冒充?內(nèi)網(wǎng)無所謂了,外網(wǎng)一般我們都用 https

          驗證(Verification):系統(tǒng)如何確保提交到每項服務中的數(shù)據(jù)是合乎規(guī)則的,不會對系統(tǒng)穩(wěn)定性、數(shù)據(jù)一致性、正確性產(chǎn)生風險?

          驗證方式

          根據(jù)協(xié)議要求,需要將憑證 Credential 放到 header Authorization 里,憑證可是是用戶名密碼,也可以是自定義生成的 TOKEN

          主流驗證方式有三大類:Basic/Digest, HMACOauth2

          1.Basic/Digest

          Digest 翻譯成摘要,是 Basic 的加強版,放在一起討論,完整定義參考 RFC2617 Basic and Digest Access Authentication[3]

          Basic 認證非常簡單,Server 發(fā)現(xiàn)沒有登錄返回 401 Unauthorized 并且攜帶 header WWW-Authenticate: Basic realm="User Visible Realm"

          Client 用戶需要將 user:password 用戶名秘密用分號 : 組合在一起,然后求 Base64 編碼,放到 header 中發(fā)送給服務端

          比如 user 是 Aladdin, 密碼是 open sesame, 此時將 (Aladdin:open sesame) 求 Base64 得到 header Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==

          但 Base64 只是編碼方式而己,和明文一樣沒有區(qū)別。所以引出了摘要算法, Digest 認證把用戶名和密碼加鹽(一個被稱為 Nonce 的變化值作為鹽值)后再通過 MD5/SHA 等哈希算法取摘要發(fā)送出去

          但是這種認證方式依然是不安全的,無論客戶端使用何種加密算法加密,無論是否采用了 Nonce 這樣的動態(tài)鹽值去抵御重放和冒認,遇到中間人攻擊時依然存在顯著的安全風險

          公司內(nèi)網(wǎng)默認可信的,可以用 Basic/Digest 隨變搞搞,但也就局限于此了

          2.HMAC

          HMAC hash-based message authentication code[4] 是市面上使用最廣泛的認證技術(shù),源自于 message authentication code[5], 主要用于消息簽名,防止被第三方修改

          使用 HMAC 需求提前生成 app key 與 access secret key 給第三方,國內(nèi)習慣稱之為 AK/SK, 我司叫做 partner-id/secret, 叫什么不重要

          AK/SK 是需要由 server 提前生成給第三方客戶端. Client 獲取待簽名的消息,使用 SK 加密后,將簽名一起發(fā)送到服務端。Server 用同樣的簽名算法 + SK 加密消息,如果一致說明訪問有效

          ?stringToSign?:=?verb?+
          ??newLineBreak?+

          ??contentType?+
          ??newLineBreak?+

          ??date?+
          ??newLineBreak?+

          ??path?+
          ??newLineBreak?+

          ??contentDigest?+
          ??newLineBreak?+

          ??//optional,?if?no?customised?header,?this?part?will?be?left?blank.
          ??partnerTokenComponent?+

          ??//optional,?if?no?customised?header,?this?part?will?be?left?blank.
          ??customisedHeaderComponent

          如何構(gòu)建消息呢?業(yè)界沒有統(tǒng)一標準,但一般都是將 method, path, date, content-type, body 柔和在一起, 其中 date 起到防止重放攻擊的作用。有時我們可以不把 body 放到消息中,有時還要把 http params 排序放到消息體中

          func?ComputeBase64EncodedHMACSHA256Signature(message?string,?secret?string)?(string,?error)?{
          ?hash?:=?hmac.New(sha256.New,?[]byte(secret))

          ?if?_,?err?:=?hash.Write([]byte(message));?err?!=?nil?{
          ??return?"",?err
          ?}

          ?val?:=?base64.StdEncoding.EncodeToring(hash.Sum(nil))

          ?return?val,?nil
          }

          至于簽名算法,我們一般用 SHA256,同時需要傳入 SK 密鑰

          感興趣的可以參考 aws s3[6] 的玩法,原理是一樣的

          我司正在廢棄遺留的 HMAC 認證方式,改用統(tǒng)一,流程更規(guī)范的 Two-Legged Oauth2 Client 認證模式

          3.Oauth2

          Oauth2[7] 是業(yè)界標準的授權(quán)協(xié)議,專注于客戶端開發(fā)人員的簡單性,同時為網(wǎng)絡應用、桌面應用、手機和客廳設備提供特定的授權(quán)流程。

          主要有以下四種模式:

          • 授權(quán)碼模式(Authorization Code)
          • 隱式授權(quán)模式(Implicit)
          • 密碼模式(Resource Owner Password Credentials)
          • 客戶端模式(Client Credentials)

          其中最常用的是 Authorization CodeClient Credentials 模式,Oauth2 涉及三個角色 owner 所有者(一般指人), server 服務端(一般指資源服務器和資源認證服務器), client 第三方客戶端,所以第一種的 Authorization Code 稱之為 Three-Legged 三腿模式

          Client Credentials 其實更像是給服務端做認證,只有 client, server 所以稱之為 Two-Legged 兩腿模式,我司就使用這種做 API 鑒權(quán)

          客戶端模式非常簡單,server 提前創(chuàng)建好 ClientID&ClientSecret 給第三方服務,然后第三方通過 ClientID&ClientSecret 調(diào)用 /oauth2/token 接口生成 token, 后續(xù)所有訪問攜帶這個 token 即可,每次由 id 服務調(diào)用 /oauth2/verify 去驗證

          舉個測試的例子:

          curl?-X?POST?https://xxxxxxxx/oauth2/token?-H?'Content-Type:?application/x-www-form-urlencoded'?-d?'client_id=c45594c80f3342ed93eea95ad4ab18bc&grant_type=client_credentials&client_secret=5aL1LOhXHi6X7E2r&scope=test.scope'

          {"access_token":"this?is?jwt?token","token_type":"Bearer","expires_in":107999}
          curl?-i?https://xxxxxxxx/v1/styles/dark.json?-H?'Authorization:?Bearer?this-is-a-jwt-token'

          指定 grant_type 為 client_credentials 同時需要填寫 scope, 非常重要,用于控制該 user 能訪問哪些資源

          和 Two-Legged 一樣,開始進行授權(quán)過程以前,第三方應用先要到授權(quán)服務器上進行注冊,所謂注冊,是指向認證服務器提供一個域名地址(用于回調(diào)),然后從授權(quán)服務器中獲取 ClientID 和 ClientSecret

          這里有幾個概念:

          • 資源所有者 owner: 一般是指我們用戶
          • 操作代理 delelegate: 一般指瀏覽器 chrome edge ...
          • 資源服務器:比如微信,比如新浪等等
          • 授權(quán)服務器:用于鑒權(quán)的,有時和資源服務器是一臺
          • 第三方應用:比如一些小程序,想訪問我的微信頭像等等

          授權(quán)過程如下:

          1. 第三方應用將資源所有者(用戶)導向授權(quán)服務器的授權(quán)頁面,并向授權(quán)服務器提供 ClientID 及用戶同意授權(quán)后的回調(diào) URI,這是一次客戶端頁面轉(zhuǎn)向

          2. 授權(quán)服務器根據(jù) ClientID 確認第三方應用的身份,用戶在授權(quán)服務器中決定是否同意向該身份的應用進行授權(quán),用戶認證的過程未定義在此步驟中,在此之前應該已經(jīng)完成

          3. 如果用戶同意授權(quán),授權(quán)服務器將轉(zhuǎn)向第三方應用在第 1 步調(diào)用中提供的回調(diào) URI,并附帶上一個授權(quán)碼和獲取令牌的地址作為參數(shù),這是第二次客戶端頁面轉(zhuǎn)向

          4. 第三方應用通過回調(diào)地址收到授權(quán)碼,然后將授權(quán)碼與自己的 ClientSecret 一起作為參數(shù),通過服務器向授權(quán)服務器提供的獲取令牌的服務地址發(fā)起請求,換取令牌。該服務器的地址應與注冊時提供的域名處于同一個域中

          5. 授權(quán)服務器核對授權(quán)碼和 ClientSecret,確認無誤后,向第三方應用授予令牌。令牌可以是一個或者兩個,其中必定要有的是訪問令牌(Access Token),可選的是刷新令牌(Refresh Token)。訪問令牌用于到資源服務器獲取資源,有效期較短,刷新令牌用于在訪問令牌失效后重新獲取,有效期較長

          6. 資源服務器根據(jù)訪問令牌所允許的權(quán)限,向第三方應用提供資源。

          理解 Oauth2 時一定要明確,哪些是通過瀏覽器訪問的,哪些是第三方服務器直接與授權(quán)服務器交互,還要注入兩次頁面轉(zhuǎn)向。建議看一下 github oauth2 或者微信的開發(fā)文檔

          JSON Web Tokens

          上面是三種主流的驗證方式,其實 Oauth2 只規(guī)定了大致框架,并沒有規(guī)定 token 如何生成。我們一般使用 JWT[8] 開放的標準(RFC 7519), 它定義了一種緊湊和獨立的方式,以 JSON 對象的形式在各方之間安全地傳輸信息。這種信息可以被驗證和信任,因為它是經(jīng)過數(shù)字簽名的,一般我們使用 RSA private key 進行簽名。

          JWT 格式由三段組成,header.payload.signature, 讓我們看個例子

          header 頭表示用什么算法進行簽名,payload 是一堆 json 可以自定義,但一些公用的己經(jīng)定義好,signature 是簽名

          HMACSHA256(
          ??base64UrlEncode(header)?+?"."?+
          ??base64UrlEncode(payload),
          ??your-256-bit-secret
          )

          大致算法也比較簡單,your-256-bit-secret 是你的密鑰,相同算法生成的 signature 一致,說明 JWT 沒有被篡改。一般都用 RSA private 去加密,然后用 public key 解密,好處是可以把 public key 放到 cdn lambda 去驗證請求是否有效。如果用對稱算法,就做不到這樣的效果

          {
          ??"alg":?"RS256",
          ??"kid":?"_default",
          ??"typ":?"JWT"
          }

          我司對 header/payload 都進行了定制,比如 Header 增加 kid 字段,代表是用哪個 RSA?key pair 進行的加密,可以用于 rotate RSA 密鑰,非常方便

          {
          ??"aud":?"c45594cXXXXXXXXXadb18bc",
          ??"exp":?1635263037,
          ??"iat":?1635155037,
          ??"iss":?"https://idp.grab.com",
          ??"jti":?"oD-VHixWQXXXXXXXXXXXAR6ctYg",
          ??"nbf":?1635154857,
          ??"pid":?"e0923XXXXXXXXXX943690d4b",
          ??"pst":?1,
          ??"scp":?"[\"7c14974d3d0e462bXXXXXXXXXXXXXXXbf7bc6\"]",
          ??"sub":?"TWO_LEGGED_OAUTH",
          ??"svc":?"",
          ??"tk_type":?"access"
          }

          payload 里面 iss 指頒發(fā)者信息,exp 是指 JWT 過期時間,如果允許篡改,那就可以無限續(xù)約了,同時增加了 partner-id, scope, two-legged 等等輸助信息。其實 TOKEN 用什么實現(xiàn)無所謂,能防止篡改的都可以,?token 搬發(fā)出去就很難控制,為了防止重放攻擊,一般要對 token 做控制

          穩(wěn)定性

          以前滴滴出過很多 ID 鑒權(quán)服務故障,原因五花八門,現(xiàn)在回頭看,都是穩(wěn)定性建設不到位

          • uuid 生成,通過 shell exec 做系統(tǒng)調(diào)用,網(wǎng)絡抖動時,司機頻繁登入登出,系統(tǒng)負載增加,直接打掛服務

          • token 相關(guān)的接口沒有 ratelimter, 直接打掛 DB

          其它的想不起來了,那一系列文章在 way 社區(qū)還蠻火的。只能說 stability 永遠的難題,尤其這種基礎(chǔ)服務

          雜談

          1. RSA public private 在 DB 不能存儲明文,要用 vault 或是 kms 加密

          2. Base64 是為了傳輸方便,省去空格特殊字符等等,但仍然是明文。hash 才是為了加密

          3. RSA 公鑰加密是不想讓別人看到內(nèi)容,因為只有私鑰才能解開。私鑰加密是為了傳遞數(shù)據(jù),不想讓別人篡改

          4. JWT TOKEN 能防篡改但是不能防重放攻擊,所以 exp 要短,同時要有 token 黑名單,還得有限流,哪怕是一小時也能把服務打爆

          5. TOKEN 是否存儲 DB 呢?存有好處,也可以選擇不存

          6. RSA 加密 Encrypt 和摘要 Digest 的區(qū)別,前者可逆,后者不可逆

          7. JWT payload 自定義內(nèi)容不易過多,一般 http header 都是有大小限制的

          8. 三個概念:編碼 Base64Encode、簽名 HMAC、加密 RSA。編碼是為了更的傳輸,等同于明文,簽名是為了信息不能被篡改,加密是為了不讓別人看到是什么信息

          9. 本文不涉及 TLS, 歷史上很多鑒權(quán)的方案都是為了應對沒有 TLS 的情況,外網(wǎng)基本都是 https, 所以很多方案現(xiàn)在己經(jīng)不適合

          10. Scope 非常重要,基于 least privilege 原則,只允許最小訪問權(quán)限,一定要控制第三方能訪問的資源

          11. 運維能力,ID 服務一般訪問受限,只有特定服務或是 admin header 才能訪問,需要提供 bot 或是網(wǎng)頁運維能力

          12. 密鑰需要定期 rotate, 業(yè)務代碼當然也要適配

          小結(jié)

          非安全及 web authentication 專業(yè),如果有描述錯誤的請大家指出~?

          寫文章不容易,如果對大家有所幫助和啟發(fā),請大家?guī)兔c擊在看,點贊分享 三連

          關(guān)于 HTTP 鑒權(quán) 大家有什么看法,歡迎留言一起討論,大牛多留言 ^_^


          — 本文結(jié)束 —


          ●?漫談設計模式在 Spring 框架中的良好實踐

          ●?顛覆微服務認知:深入思考微服務的七個主流觀點

          ●?人人都是 API 設計者

          ●?一文講透微服務下如何保證事務的一致性

          ●?要黑盒測試微服務內(nèi)部服務間調(diào)用,我該如何實現(xiàn)?



          關(guān)注我,回復 「加群」 加入各種主題討論群。



          對「服務端思維」有期待,請在文末點個在看

          喜歡這篇文章,歡迎轉(zhuǎn)發(fā)、分享朋友圈


          在看點這里
          瀏覽 68
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  色婷婷资源网 | 超碰最新在线观看 | 苍井空一二区 | 操操网网址 | 91操B|