本文对服务端API每一次HTTP或者HTTPS协议请求的签名机制进行了说明,并提供了对应的示例代码。

简介

对于每一次HTTP或者HTTPS协议请求,阿里云会根据访问中的签名信息验证访问请求者身份。具体由使用AccessKeyID和AccessKeySecret对称加密验证实现,AccessKeyID和AccessKeySecret可在控制台AccessKey管理页面获得。
  • AccessKeyID:访问者身份
  • AccessKeySecret:加密签名字符串和服务器端验证签名字符串的密钥,必须严格保密谨防泄露。
说明 阿里云提供了多种编程语言的SDK及第三方SDK,可以免去您签名的烦恼。更多详情,请下载SDK

构造字符串

  1. 构造规范化请求字符串。
    1. 排序参数:
      排序规则以首字母顺序排序,排序参数包括公共参数和接口自定义参数,不包括公共请求参数中的Signature参数。
    2. 编码参数:
      使用 UTF-8 字符集按照RFC 3986规则编码请求参数和参数取值,编码规则如下:
      • 字符 A~Z、a~z、0~9 以及字符-_.~不编码。
      • 其它字符编码成%XY的格式,其中XY是字符对应ASCII码的16进制。示例:半角双引号"对应%22
      • 扩展的UTF-8字符,编码成%XY%ZA…的格式。
      • 空格编码成%20,而不是加号+
        说明 该编码方式与application/x-www-form-urlencoded MIME格式编码算法相似,但又有所不同。如果您使用的是Java标准库中的java.net.URLEncoder,可以先用标准库中percentEncode编码,随后将编码后的字符中加号+替换为%20、星号*替换为%2A%7E替换为波浪号~,即可得到上述规则描述的编码字符串。
    3. 使用等号=连接编码后的请求参数和参数取值。
    4. 使用与号&连接编码后的请求参数。
      注意 参数排序与上方排序参数一致。
  2. 构造签名字符串。
    1. 构造待签名字符串 StringToSign:
      您可以同样使用percentEncode处理上一步构造的规范化请求字符串,示例如下:
      StringToSign=
      HTTPMethod + "&" + //HTTPMethod:发送请求的 HTTP 方法,例如 GET。
      percentEncode("/") + "&" + //percentEncode("/"):字符(/)UTF-8 编码得到的值,即 %2F。
      percentEncode(CanonicalizedQueryString) //您的规范化请求字符串。
    2. 计算HMAC值:
      按照RFC 2104的定义,使用上述步骤得到的字符串计算签名 HMAC 值。
      注意 计算签名时使用的Key就是您持有的AccessKeySecret并加上一个&字符(ASCII:38),使用的哈希算法是SHA1。
    3. 计算签名值:
      按照BASE64编码规则把上一步骤中HMAC值编码成字符串,即得到签名值Signature
    4. 添加签名:
      将得到的签名值作为Signature参数添加到请求参数中,即完成请求签名过程。
      注意 得到的签名值在作为最后的请求参数值提交给服务器时,也要按照RFC 3986规则进行 URL 编码。

请求示例

  1. 以GetVideoPlayAuth为例,假设使用的AccessKeyIdtestAccessKeyIdAccessKeySecrettestAccessKeySecret。 那么签名前的请求URL,示例如下:
    http://vod.cn-shanghai.aliyuncs.com/?TimeStamp=2017-10-10T12:02:54Z&Format=JSON&AccessKeyId=testAccessKeyId&Action=GetVideoPlayAuth&SignatureMethod=HMAC-SHA1&SignatureNonce=8f8a035d-6496-4268-afd4-67c22837e38d&Version=2017-03-21&SignatureVersion=1.0&VideoId=5aed81b74ba84920be578cdfe004af4b
                
  2. 计算得到的待签名字符串StringToSign 如下:
    GET&%2F&AccessKeyId%3DtestAccessKeyId%26Action%3DGetVideoPlayAuth%26Format%3DJSON%26SignatureMethod%3DHMAC-SHA1%26SignatureNonce%3D8f8a035d-6496-4268-afd4-67c22837e38d%26SignatureVersion%3D1.0%26Timestamp%3D2017-10-10T12%253A02%253A54Z%26Version%3D2017-03-21%26VideoId%3D5aed81b74ba84920be578cdfe004af4b
    
  3. 因为AccessKeySecrettestAccessKeySecret,所以用于计算HMAC的Key为testAccessKeySecret&,计算得到的签名值如下:
     Ibgh7y8Vp47LBuAsf5Xhi1SvDss%3D
                
  4. 将签名作为Signature参数加入到URL请求中,最后得到的URL如下:
    http://vod.cn-shanghai.aliyuncs.com?AccessKeyId=testAccessKeyId&Action=GetVideoPlayAuth&Format=JSON&SignatureMethod=HMAC-SHA1&SignatureNonce=8f8a035d-6496-4268-afd4-67c22837e38d&SignatureVersion=1.0&Timestamp=2017-10-10T12%3A02%3A54Z&Version=2017-03-21&VideoId=5aed81b74ba84920be578cdfe004af4b&Signature=Ibgh7y8Vp47LBuAsf5Xhi1SvDss%3D

Java 示例代码

以下将为您介绍OpenAPI公共参数中需要您通过代码生成的参数,其他参数请根据文档填写具体值。
说明 本示例不需要依赖第三方的库包,可以直接使用。
  1. 生成签名串Signature。
    1. 构造规范化的请求字符串:
      参数排序及URL编码,参数排序是指将参数名称以字母序升序排列。示例如下:
      /*对所有参数名称和参数值做URL编码*/
      public static List<String> getAllParams(Map<String, String> publicParams, Map<String, String> privateParams) {
          List<String> encodeParams = new ArrayList<String>();
          if (publicParams != null) {
              for (String key : publicParams.keySet()) {
                  String value = publicParams.get(key);
                  //将参数和值都urlEncode一下。
                  String encodeKey = percentEncode(key);
                  String encodeVal = percentEncode(value);
                  encodeParams.add(encodeKey + "=" + encodeVal);
              }
          }
          if (privateParams != null) {
              for (String key : privateParams.keySet()) {
                  String value = privateParams.get(key);
                  //将参数和值都urlEncode一下。
                  String encodeKey = percentEncode(key);
                  String encodeVal = percentEncode(value);
                  encodeParams.add(encodeKey + "=" + encodeVal);
              }
          }
          return encodeParams;
      }
      /*获取 CanonicalizedQueryString*/
      public static String getCQS(List<String> allParams) {
          ParamsComparator paramsComparator = new ParamsComparator();
          Collections.sort(allParams, paramsComparator);
          String cqString = "";
          for (int i = 0; i < allParams.size(); i++) {
              cqString += allParams.get(i);
              if (i != allParams.size() - 1) {
                  cqString += "&";
              }
          }
          return cqString;
      }
      /*字符串参数比较器,按字母序升序*/
      public static class ParamsComparator implements Comparator<String> {
          @Override
          public int compare(String lhs, String rhs) {
              return lhs.compareTo(rhs);
          }
      }
                  
    2. 构造待签名的字符串,示例如下:
      /*构造待签名的字符串*/
      String StringToSign = httpMethod + "&" + percentEncode("/") + "&" + percentEncode(CanonicalizedQueryString);
      /*特殊字符替换为转义字符*/
      public static String percentEncode(String value) {
        try {
          String urlEncodeOrignStr = URLEncoder.encode(value, "UTF-8");
          String plusReplaced = urlEncodeOrignStr.replace("+", "%20");
          String starReplaced = plusReplaced.replace("*", "%2A");
          String waveReplaced = starReplaced.replace("%7E", "~");
          return waveReplaced;
        } catch (UnsupportedEncodingException e) {
          e.printStackTrace();
        }
        return value;
      }
                  
    3. 计算待签名字符串的HMAC值,示例如下:
      public static byte[] hmacSHA1Signature(String accessKeySecret, String stringToSign) {
          try {
              String key = accessKeySecret + "&";
              try {
                    SecretKeySpec signKey = new SecretKeySpec(key.getBytes(), "HmacSHA1");
                    Mac mac = Mac.getInstance("HmacSHA1");
                    mac.init(signKey);
                    return mac.doFinal(stringToSign.getBytes());
              } catch (Exception e) {
                    throw new SignatureException("Failed to generate HMAC : " + e.getMessage());
              }
          } catch (SignatureException e) {
              e.printStackTrace();
          }
          return null;
      }
                  
    4. 编码得到最终签名值:
      按照 Base64 编码规则将1.3中计算得到的HMAC值编码成字符串,得到最终签名值(Signature),示例如下:
      public static String newStringByBase64(byte[] bytes)
               throws UnsupportedEncodingException {
          if (bytes == null || bytes.length == 0) {
              return null;
          }
          return new String(new BASE64Encoder().encode(bytes));
      }
                  
  2. 生成时间戳TimeStamp。
    生成TimeStamp,为UTC时间戳,如:2017-10-10T12:02:54Z,示例如下:
    /*生成当前UTC时间戳Time*/
    public static String generateTimestamp() {
        Date date = new Date(System.currentTimeMillis());
        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
        df.setTimeZone(new SimpleTimeZone(0, "GMT"));
        return df.format(date);
    }
                
  3. 生成随机数SignatureNonce,示例如下:
    public static String generateRandom() {
        String signatureNonce = UUID.randomUUID().toString();
        return signatureNonce;
    }         

更多参考

可以直接使用服务端接口SDK,来避免自行实现签名机制,SDK支持以下语言版本:
说明 服务端接口SDK封装了点播服务API的使用,也提供了各接口的代码示例。

其它签名示例

如确实想自行计算签名,可参考阿里云SDK的签名实现代码: