后端签名插件

后端签名密钥是由您创建的一对 Key 和 Secret,相当于您给API网关颁发了一个账号密码。API绑定后端签名插件后 ,API 网关向您后端服务请求时会使用这一对Key和Secret对请求内容进行加签处理,您后端服务可以对网关发送过来的请求做对称加签计算,对比网关的签名和服务器端计算的签名是否一致就可以对网关做身份验证。

1. 什么是后端签名

后端签名(原名:签名密钥)是由您创建的一对 Key 和 Secret,相当于您给网关颁发了一个账号密码,网关向您后端服务请求时会将密钥计算后一起传过去,您后端获取相应的字符串做对称计算,就可以对网关做身份验证。若您的后端服务为VPC环境,且通过内网对接(专属网络VPC环境开放API),您无需使用后端签名,通道自身是安全的。

原签名密钥功能现在合并进了插件体系。现存的控制台界面与接口仍然可以使用,原签名密钥功能与后端签名插件属于同一种插件类型,受到同类型插件绑定的限制。

使用原签名密钥功能接口或控制台创建或更改,会同步数据至插件系统,但不会反向同步。

2. 插件绑定

您将密钥绑定到 API 之后,由网关发送给您服务后端的该 API 的请求均会加上签名信息。您需要在后端做对称计算来解析签名信息,从而验证网关的身份。

当您想替换某个API的密钥时,请直接修改要修改插件中的key和secret, 已绑定的API将会即时生效。

3. 插件配置

可以选择JSON或者YAML格式的来配置您的插件,两种格式的schema相同,可以搜索yaml to json转换工具来进行配置格式的转换,YAML格式的模板见下表

---
type: APIGW_BACKEND
key: SampleKey
secret: SampleSecret
                        

4. 读取 API 网关签名方法

网关计算的签名保存在 Request 的 Header 中,Header 名称:X-Ca-Proxy-Signature。

5. 后端签名规则

签名计算的详细 demo(JAVA)请参照链接:https://github.com/aliyun/api-gateway-demo-sign-backend-java。

签名计算方法步骤如下:

  1. API网关需要从发送给后端的HTTP请求中提取出关键数据,组合成一个签名串,生成的签名串的格式如下:

    HTTPMethod
    Content-MD5
    Headers
    PathAndParameters

    以上4个字段构成整个签名串,字段之间使用\n间隔,如果Headers为空,则不需要加\n,其他字段如果为空都需要保留\n。签名大小写敏感。下面介绍下每个字段的提取规则:

    • HTTPMethod:HTTP的方法,全部大写,比如POST

    • Content-MD5:网关会从客户端请求Header中读取Content-MD5头的值,如果客户端不传这个Content-MD5头,网关的签名串的Content-MD5的值为空。客户端只有在请求存在Body且Body为非Form形式时才需要计算并且传输Content-MD5头。下面是Java的Content-MD5值的参考计算方式:

    String content-MD5 = Base64.encodeBase64(MD5(bodyStream.getbytes("UTF-8")));

    • Headers:Headers 指所有参与签名计算的 Header的Key、Value。参与签名计算的 Header 的 Key 从 Request Header 中读取,Key为:"X-Ca-Proxy-Signature-Headers",多个 Key 用英文逗号分割。先对所有参与签名计算的 Header 的 Key 按照字典排序,然后将 Header 的 Key 转换成小写后按照如下方式拼接:

    String headers = HeaderKey1.toLowerCase() + ":" + HeaderValue1 +"\n"+
     HeaderKey2.toLowerCase() + ":" + HeaderValue2 +"\n"+
     ... +
    HeaderKeyN.toLowerCase() + ":" + HeaderValueN + "\n"

    • PathAndParameters

    这个字段包含Path,Query和Form中的所有参数,组织方法: 如果有 Query 或 Form 参数则加 ?,然后对 Query+Form 参数按照字典对 Key 进行排序后按照如下方法拼接,如果没有 Query 或 Form 参数,则 PathAndParameters = Path

    String PathAndParameters =
     Path +
     "?" +
     Key1 + "=" + Value1 
    + "&" + Key2 + "=" + Value2 +
     ... 
    "&" + KeyN + "=" + ValueN

    说明 这里 Query 或 Form 参数的 Value 可能有多个,多个的时候只取第一个 Value 参与签名计算; 只要传递了的参数,签名过程中的等号“=“无论什么情况都需要保留,比如这两个query参数传递时的形式:"path?a=&b",签名时需要写成:"path?a=&b="。

  2. 计算签名:

Mac hmacSha256 = Mac.getInstance("HmacSHA256");
byte[] keyBytes = secret.getBytes("UTF-8");  //secret 为绑定到 API 上的签名密钥
hmacSha256.init(new SecretKeySpec(keyBytes, 0, keyBytes.length, "HmacSHA256"));
String sign = new String(Base64.encodeBase64(Sha256.doFinal(stringToSign.getBytes("UTF-8")),"UTF-8"));

抽象一下就是将串(StringToSign)使用UTF-8解码后得到Byte数组,然后使用加密算法对Byte数组进行加密,然后使用Base64算法进行编码,形成最终的签名。

6. 调试模式

为了方便后端签名接入与调试,可以开启 Debug 模式进行调试,具体方法如下: 请求API网关的 Header 中添加 X-Ca-Request-Mode = debug。

后端服务在 Header 中读取 X-Ca-Proxy-Signature-String-To-Sign 即可,因为 HTTP Header 中值不允许有换行符,因此换行符被替换成了 "#"。

注意:X-Ca-Proxy-Signature-String-To-Sign 不参与后端签名计算。

7. 时间戳校验

如果后端需要对请求进行时间戳校验,可以在 API 定义中选择系统参数 "CaRequestHandleTime" ,值为网关收到请求的格林威治时间。