hmac-auth外掛程式實現了基於HMAC演算法為HTTP請求產生不可偽造的簽名,並基於簽名實現身份認證和鑒權。本文介紹如何配置hmac-auth外掛程式。
外掛程式類型
認證鑒權。
配置欄位
認證配置
名稱 | 資料類型 | 填寫要求 | 預設值 | 描述 |
consumers | array of object | 必填。 | - | 佈建服務的調用者,用於對請求進行認證。 |
date_offset | number | 選填。 | - | 配置允許的用戶端最大時間位移,單位為秒。根據要求標頭 |
global_auth | array of string | 選填(**僅執行個體層級配置**) | - | 只能在執行個體層級配置,若配置為true,則全域生效認證機制; 若配置為false,則只對做了配置的網域名稱和路由生效認證機制,若不配置則僅當沒有網域名稱和路由配置時全域生效(相容老使用者使用習慣)。 |
子項consumers中每一項的配置欄位說明如下。
名稱 | 資料類型 | 填寫要求 | 預設值 | 描述 |
key | string | 必填。 | - | 配置該consumer的訪問憑證。 |
secret | string | 必填。 | - | 配置用於產生簽名的secret。 |
name | string | 必填。 | - | 配置該consumer的名稱。 |
鑒權配置(非必需)
名稱 | 資料類型 | 填寫要求 | 預設值 | 描述 |
allow | array of string | 選填(**非執行個體層級配置**)。 | - | 只能在路由或網域名稱等細粒度規則上配置,對於符合匹配條件的請求,配置允許訪問的 consumer,從而實現細粒度的許可權控制。 |
在一個規則裡,鑒權配置和認證配置不可同時存在。
對於通過認證鑒權的請求,請求的Header會被添加一個
X-Mse-Consumer欄位,用以標識調用者的名稱。
配置樣本
全域配置認證和路由粒度進行鑒權
以下配置將對網關特定路由或網域名稱開啟 Hmac Auth 認證和鑒權。注意key欄位不能重複。
在執行個體層級做如下外掛程式配置:
global_auth: false
consumers:
- key: appKey-example-1
secret: appSecret-example-1
name: consumer-1
- key: appKey-example-2
secret: appSecret-example-2
name: consumer-2route-a和route-b兩個路由做如下外掛程式配置:
allow:
- consumer1在*.example.com和test.com兩個網域名稱做如下外掛程式配置:
allow:
- consumer2此例指定的
route-a和route-b即在建立網關路由時填寫的路由名稱,當匹配到這兩個路由時,將允許name為consumer1的調用者訪問,其他調用者不允許訪問。此例指定的
*.example.com和test.com用於匹配請求的網域名稱,當發現網域名稱匹配時,將允許name為consumer2的調用者訪問,其他調用者不被允許訪問。
網關執行個體層級開啟
global_auth: true
consumers:
- key: appKey-example-1
secret: appSecret-example-1
name: consumer-1
- key: appKey-example-2
secret: appSecret-example-2
name: consumer-2簽名機制說明
配置準備
如上指引,在外掛程式配置中配置產生和驗證簽名所需的憑證配置。
key: 用於要求標頭
x-ca-key中設定。secret: 用於產生請求籤名。
用戶端簽名產生方式
流程簡介
用戶端產生簽名共分三步處理。
從原始請求中提取關鍵資料,得到一個用來簽名的字串。
使用密碼編譯演算法和配置的
secret對關鍵資料簽名串進行加密處理,得到簽名。將簽名所相關的所有頭加入到原始HTTP請求中,得到最終HTTP請求。
簽名串提取流程
用戶端需要從HTTP請求中提取出關鍵資料,組合成一個簽名串,產生的簽名串的格式如下。
HTTPMethod
Accept
Content-MD5
Content-Type
Date
Headers
PathAndParameters以上7個欄位構成整個簽名串,欄位之間使用\n間隔,如果Headers為空白,則不需要加\n,其他欄位如果為空白都需要保留\n。簽名大小寫敏感。
每個欄位的擷取規則如下。
HTTPMethod:HTTP的方法,全部大寫,比如POST。
Accept:請求中的Accept頭的值,可為空白。建議顯式設定 Accept Header。當 Accept 為空白時,部分HTTP用戶端會給Accept設定預設值為
*/*,導致簽名校正失敗。Content-MD5:請求中的Content-MD5頭的值,可為空白。當請求存在Body且Body為非Form形式時才計算Content-MD5頭(請確保在要求標頭中添加Content-MD5 ),下面是Java的Content-MD5值的參考計算方式。
String content-MD5 = Base64.encodeBase64(MD5(bodyStream.getbytes("UTF-8")));Content-Type:請求中的Content-Type頭的值,可為空白。
Date:請求中的Date頭的值,當未開啟
date_offset配置時,可為空白,否則將用於時間位移校正。Headers:您可以選取指定的Header參與簽名,關於Header的簽名串拼接方式有以下規則。
參與簽名計算的Header的Key按照字典排序後使用如下方式拼接。
HeaderKey1 + ":" + HeaderValue1 + "\n"\+ HeaderKey2 + ":" + HeaderValue2 + "\n"\+ ... HeaderKeyN + ":" + HeaderValueN + "\n"某個Header的Value為空白,則使用HeaderKey+":"+"\n"參與簽名,需要保留Key和英文冒號。
所有參與簽名的Header的Key的集合使用英文逗號分割放到Key為
X-Ca-Signature-Headers的Header中。以下Header不參與Header簽名計算:X-Ca-Signature、X-Ca-Signature-Headers、Accept、Content-MD5、Content-Type、Date。
PathAndParameters: 這個欄位包含Path、Query和Form中的所有參數,具體組織形式如下。
Path + "?" + Key1 + "=" + Value1 + "&" + Key2 + "=" + Value2 + ... "&" + KeyN + "=" + ValueN
Query和Form參數對的Key按照字典排序後使用上面的方式拼接。
Query和Form參數為空白時,則直接使用Path,不需要添加。
Query和Form存在數組參數時(Key相同,Value不同的參數) ,取第一個Value參與簽名計算。
簽名串提取樣本
初始的HTTP請求。
POST /http2test/test?param1=test HTTP/1.1
host:api.aliyun.com
accept:application/json; charset=utf-8
ca_version:1
content-type:application/x-www-form-urlencoded; charset=utf-8
x-ca-timestamp:1525872629832
date:Wed, 09 May 2018 13:30:29 GMT+00:00
user-agent:ALIYUN-ANDROID-DEMO
x-ca-nonce:c9f15cbf-f4ac-4a6c-b54d-f51abf4b5b44
content-length:33
username=xiaoming&password=123456789產生的正確簽名串。
POST
application/json; charset=utf-8
application/x-www-form-urlencoded; charset=utf-8
Wed, 09 May 2018 13:30:29 GMT+00:00
x-ca-key:203753385
x-ca-nonce:c9f15cbf-f4ac-4a6c-b54d-f51abf4b5b44
x-ca-signature-method:HmacSHA256
x-ca-timestamp:1525872629832
/http2test/test?param1=test&password=123456789&username=xiaoming簽名計算流程
用戶端從HTTP請求中提取出關鍵資料群組裝成簽名串後,需要對簽名串進行加密及編碼處理,形成最終的簽名。
具體的加密形式如下,其中stringToSign是提取出來的簽名串,secret是外掛程式配置中填寫的,sign是最終產生的簽名。
Mac hmacSha256 = Mac.getInstance("HmacSHA256");
byte[] secretBytes = secret.getBytes("UTF-8");
hmacSha256.init(new SecretKeySpec(secretBytes, 0, secretBytes.length, "HmacSHA256"));
byte[] result = hmacSha256.doFinal(stringToSign.getBytes("UTF-8"));
String sign = Base64.encodeBase64String(result);將stringToSign使用UTF-8解碼後得到Byte數組,然後使用密碼編譯演算法對Byte數組進行加密,然後使用Base64演算法進行編碼,形成最終的簽名。
添加簽名流程
用戶端需要將以下四個Header放在HTTP請求中傳輸給API Gateway,進行簽名校正。
x-ca-key:取值APP Key。必選。
x-ca-signature-method:簽名演算法。取值HmacSHA256或者HmacSHA1,可選,預設值為HmacSHA256。
x-ca-signature-headers:所有簽名頭的Key的集合。使用英文逗號分隔,可選。
x-ca-signature:簽名。必選。
下面是攜帶簽名的整個HTTP請求的樣本。
POST /http2test/test?param1=test HTTP/1.1
host:api.aliyun.com
accept:application/json; charset=utf-8
ca_version:1
content-type:application/x-www-form-urlencoded; charset=utf-8
x-ca-timestamp:1525872629832
date:Wed, 09 May 2018 13:30:29 GMT+00:00
user-agent:ALIYUN-ANDROID-DEMO
x-ca-nonce:c9f15cbf-f4ac-4a6c-b54d-f51abf4b5b44
x-ca-key:203753385
x-ca-signature-method:HmacSHA256
x-ca-signature-headers:x-ca-timestamp,x-ca-key,x-ca-nonce,x-ca-signature-method
x-ca-signature:xfX+bZxY2yl7EB/qdoDy9v/uscw3Nnj1pgoU+Bm6xdM=
content-length:33
username=xiaoming&password=123456789服務端簽名驗證方式
流程簡介
伺服器驗證用戶端簽名一共分四步處理。
從接收到的請求中提取關鍵資料,得到一個用來簽名的字串。
從接收到的請求中讀取
key,通過key查詢到對應的secret。使用密碼編譯演算法和
secret對關鍵資料簽名串進行加密處理,得到簽名。從接收到的請求中讀取用戶端簽名,對比伺服器端簽名和用戶端簽名的一致性。
簽名排錯方法
網關簽名校正失敗時,會將服務端的簽名串(StringToSign)放到HTTP Response的Header中返回到用戶端,Key為:X-Ca-Error-Message,您只需要將本地計算的簽名串(StringToSign)與服務端返回的簽名串進行對比即可找到問題。
如果服務端與用戶端的StringToSign一致請檢查用於簽名計算的APP Secret是否正確。
因為HTTP Header中無法表示換行,因此StringToSign中的分行符號都被替換成#,如下所示。
X-Ca-Error-Message: Server StringToSign:`GET#application/json##application/json##X-Ca-Key:200000#X-Ca-Timestamp:1589458000000#/app/v1/config/keys?keys=TEST`相關錯誤碼
HTTP狀態代碼 | 出錯資訊 | 原因說明 |
400 | Invalid Signature. | 要求標頭x-ca-signature簽名串,與服務端計算得到簽名不一致。 |
400 | Invalid Content-MD5. | 要求標頭content-md5不正確。 |
400 | Invalid Date. | 根據要求標頭date計算時間位移超過配置的date_offset。 |
401 | Invalid Key. | 要求標頭未提供x-ca-key,或者x-ca-key無效。 |
401 | Empty Signature. | 要求標頭未提供x-ca-signature簽名串。 |
403 | Unauthorized Consumer. | 請求的調用方無存取權限。 |
413 | Request Body Too Large. | 請求Body超過限制大小:32MB。 |
413 | Payload Too Large. | 請求Body超過全域配置DownstreamConnectionBufferLimits,請在參數配置頁調高此項。 |
注意謹慎調高DownstreamConnectionBufferLimits此參數配置,調高後網關記憶體使用量將有顯著增加。