背景
由於DEF工程體系的歷史原因,很多工程服務並未註冊至開放閘道器而是私自開放介面,每個服務都維護一個client身份表,同一個client在不同開放服務間同步身份資料困難。
在使用過程中,呼叫方申請client流程割裂、服務認證功能後置導致每個服務提供方認證邏輯同質化、無開放介面許可權管控等功能影響服務的開放安全及client接入體感,DEF開放閘道器統一認證服務旨在通過流程上規範client申請鏈路,同時在client申請時指定開放服務和對應許可權介面,由閘道器統一認證服務實現身份認證、許可權管控,並通過Oauth2授權搭配JWT機制為接入服務提供高效能認證互信方案,消除開放服務獨立認證與授權壁壘,保證所有開放服務許可權管控自動化。
必要性
當前開放服務,如雲構建、disp、def-flow和def-work等都各自維護一套基於開放的認證和許可權體系,呼叫方在開放平臺申請的token經閘道器審批後,仍需要由呼叫方聯絡對應開放服務負責人提供元資訊、呼叫許可權,由開放服務負責人手動錄入各自系統進行二級鑑權。
由於開放平臺並未與開放服務進行資料打通,因此很難做到自動化,對使用者而言申請client的體感較差,對服務管理員而言需手動新增使用者元資訊,體感也很差。
在執行階段,經歷了多次認證與鑑權,特別是每個開放服務側都需實現一套簡單認證系統,每次請求都需查詢DB造成效能開銷,無法保證開放介面的效能一致性。
因此,急需解決開放服務認證體系與開放閘道器認證統一的問題,並且從流程上簡化客戶端接入時開放服務側的工作,最終也要保證各服務現有認證的相容,對呼叫方和客戶端透明。
統一認證服務方案探究
● 相容模式,微(開放)服務仍負責各自許可權
● OAuth2授權模式,由閘道器認證中心統一管理開放服務許可權
● OAuth2授權 + JWT驗證,閘道器認證中心授權,開放服務本地認證
探索1.相容模式
相容模式不改變開放服務已有的認證體系,仍然由服務自身進行認證與鑑權。但:
- 服務接入體驗差,每個開放服務需提供接入開放閘道器許可權體系的開放介面和補償介面(介面由於網路抖動或其他原因請求失敗,需呼叫補償介面保證資料與之前一致),並保證冪等
- 邏輯複雜,開放閘道器需維護分散式事務
- 執行時效能瓶頸,開放服務的認證每次都查表,RT大,統一治理成本高
相容模式僅僅是對現狀的妥協並不符合我們的訴求,且引入了其他治理成本,因此放棄。
探索2.基於OAuth2的授權認證
OAuth2有幾種授權模式 -- 授權碼、密碼、隱藏式和憑證式,常用的是授權碼模式,它的前提是有介面且針對使用者是人的情況;憑證模式則是針對服務的一種授權,通過提供憑證(如AK/SK)來換取token,它互動流程簡單,針對服務級別,適合內部應用使用,也契合API場景。
OAuth2的角色對應
● 客戶端:使用Client的二方服務
● 資源所有者:開放服務,如def-work
● 資源伺服器:開放服務
● 授權伺服器:開放平臺
客戶端接入通過BPMS流程管控,依次由閘道器負責人、依賴的開放服務負責人審批,流程通過後則認為當前客戶端為可信客戶端,由業務閘道器認證後授權給開放服務使用。
該方案可以做到以下保證:
● 認證與許可權收斂到閘道器,開放服務無需自建認證體系
● 客戶端接入流程化,可信應用追溯
● “許可權校驗模組”對開放服務透明,整合在閘道器外掛中
● token有有效期,且可被即時撤銷,安全風險可控
但是,由於存在服務許可權校驗模組rpc呼叫閘道器Oauth2模組獲取許可權資訊,因此存在些許不足:
- “許可權校驗模組”需RPC閘道器OAuth2驗證token以及許可權,效能、流量壓力較大
- 閘道器側OAuth2模組單點風險,若掛則所有開放介面均不可用
那麼是否有一種自簽名的token,該token簽發後可由客戶端自行驗證並獲取內部相關資訊,並且保證token使用期可控呢?這樣就替開放平臺認證中心省下來流量與db等多重壓力,而且提升認證鏈路的效能,最終找到了JWT。
最終方案,基於OAuth2授權和JWT本地鑑權
JWT最大的缺點是簽發的token無法立即撤銷,需等待其超時失效。但由於我們的場景是API閘道器以及開放介面呼叫,本身是無狀態場景,閘道器側對每個請求都需進行一次性認證,因此token的安全失效問題便可以迎刃而解。
而優點自不必說,
● 效能優勢,開放服務本地校驗速度有保證
● 一次性認證,token用完即失效,有安全保證
使用體感
使用者在開放平臺申請client時,選擇對應開放服務及重點介面時會走特殊流程審批,
對應管理員審批通過後,AK/SK即可使用
。
使用該AK/SK請求介面時,認證中心會對使用者請求進行認證與介面鑑權,認證失敗直接返回,認證通過向開放服務下發token,開放服務側接入的閘道器外掛會本地解析token,拿到使用者資訊並儲存在請求上下文
ctx.defauth = {
clientName: 'xxxx',
clientCredential: 'xxxx',
adminId: 'xxxx'
}
針對已有client表的開放服務,可自行upsert使用者資料至表中即可實現相容,無需人工輸入;若服務無需儲存使用者資訊,可直接信任該認證資訊處理請求即可。