Sample-Code
所有文档

          相关参考 Reference

          Sample-Code

          Python示例

          假设用户向北京的BOS集群使用UploadPart接口上传一个文件的最后一个Part,内容为Example

          • Bucket name:test
          • Object key:myfolder/readme.txt
          • uploadId:a44cc9bab11cbd156984767aad637851
          • partNumber:9
          • Access Key ID:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
          • Secret Access Key:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
          • 时间:北京时间2015年4月27日16点23分49秒(转换为UTC时间是2015年4月27日8点23分49秒)

          则其HTTP请求如下:

          PUT /test/myfolder/readme.txt?partNumber=9&uploadId=a44cc9bab11cbd156984767aad637851 HTTP/1.1
          Host: bj.bcebos.com
          Date: Mon, 27 Apr 2015 16:23:49 +0800
          Content-Type: text/plain
          Content-Length: 8
          Content-Md5: NFzcPqhviddjRNnSOGo4rw==
          x-bce-date: 2015-04-27T08:23:49Z
          
          Example

          用户可根据上述HTTP请求填写以下函数中的各个字段。

           if __name__ == "__main__":
               credentials = BceCredentials("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb")
               http_method = "PUT"
               path = "/test/myfolder/readme.txt"
               headers = {"host": "bj.bcebos.com",
                          "content-length": 8,
                          "content-md5": "NFzcPqhviddjRNnSOGo4rw==",
                          "content-type":"text/plain",
                          "x-bce-date": "2015-04-27T08:23:49Z"}
               params = {"partNumber": 9,
                         "uploadId": "a44cc9bab11cbd156984767aad637851"}
               timestamp = 1430123029
               result = sign(credentials, http_method, path, headers, params, timestamp)
               print result

          请点击此处获取完整代码

          完整代码内容如下:

           # -*- coding: UTF-8 -*-
           import hashlib
           import hmac
           import string
           import datetime
           
           
           AUTHORIZATION = "authorization"
           BCE_PREFIX = "x-bce-"
           DEFAULT_ENCODING = 'UTF-8'
           
           
           # 保存AK/SK的类
           class BceCredentials(object):
               def __init__(self, access_key_id, secret_access_key):
                   self.access_key_id = access_key_id
                   self.secret_access_key = secret_access_key
           
           
           # 根据RFC 3986,除了:
           #   1.大小写英文字符
           #   2.阿拉伯数字
           #   3.点'.'、波浪线'~'、减号'-'以及下划线'_'
           # 以外都要编码
           RESERVED_CHAR_SET = set(string.ascii_letters + string.digits + '.~-_')
           def get_normalized_char(i):
               char = chr(i)
               if char in RESERVED_CHAR_SET:
                   return char
               else:
                   return '%%%02X' % i
           NORMALIZED_CHAR_LIST = [get_normalized_char(i) for i in range(256)]
           
           
           # 正规化字符串
           def normalize_string(in_str, encoding_slash=True):
               if in_str is None:
                   return ''
           
               # 如果输入是unicode,则先使用UTF8编码之后再编码
               in_str = in_str.encode(DEFAULT_ENCODING) if isinstance(in_str, unicode) else str(in_str)
           
               # 在生成规范URI时。不需要对斜杠'/'进行编码,其他情况下都需要
               if encoding_slash:
                   encode_f = lambda c: NORMALIZED_CHAR_LIST[ord(c)]
               else:
                   # 仅仅在生成规范URI时。不需要对斜杠'/'进行编码
                   encode_f = lambda c: NORMALIZED_CHAR_LIST[ord(c)] if c != '/' else c
           
               # 按照RFC 3986进行编码
               return ''.join([encode_f(ch) for ch in in_str])
           
           
           # 生成规范时间戳
           def get_canonical_time(timestamp=0):
               # 不使用任何参数调用的时候返回当前时间
               if timestamp == 0:
                   utctime = datetime.datetime.utcnow()
               else:
                   utctime = datetime.datetime.utcfromtimestamp(timestamp)
           
               # 时间戳格式:[year]-[month]-[day]T[hour]:[minute]:[second]Z
               return "%04d-%02d-%02dT%02d:%02d:%02dZ" % (
                   utctime.year, utctime.month, utctime.day,
                   utctime.hour, utctime.minute, utctime.second)
           
           
           # 生成规范URI
           def get_canonical_uri(path):
               # 规范化URI的格式为:/{bucket}/{object},并且要对除了斜杠"/"之外的所有字符编码
               return normalize_string(path, False)
           
           
           # 生成规范query string
           def get_canonical_querystring(params):
               if params is None:
                   return ''
           
               # 除了authorization之外,所有的query string全部加入编码
               result = ['%s=%s' % (normalize_string(k), normalize_string(v)) for k, v in params.items() if k.lower != AUTHORIZATION]
           
               # 按字典序排序
               result.sort()
           
               # 使用&符号连接所有字符串并返回
               return '&'.join(result)
           
           
           # 生成规范header
           def get_canonical_headers(headers, headers_to_sign=None):
               headers = headers or {}
           
               # 没有指定header_to_sign的情况下,默认使用:
               #   1.host
               #   2.content-md5
               #   3.content-length
               #   4.content-type
               #   5.所有以x-bce-开头的header项
               # 生成规范header
               if headers_to_sign is None or len(headers_to_sign) == 0:
                   headers_to_sign = {"host", "content-md5", "content-length", "content-type"}
           
               # 对于header中的key,去掉前后的空白之后需要转化为小写
               # 对于header中的value,转化为str之后去掉前后的空白
               f = lambda (key, value): (key.strip().lower(), str(value).strip())
           
               result = []
               for k, v in map(f, headers.iteritems()):
                   # 无论何种情况,以x-bce-开头的header项都需要被添加到规范header中
                   if k.startswith(BCE_PREFIX) or k in headers_to_sign:
                       result.append("%s:%s" % (normalize_string(k), normalize_string(v)))
           
               # 按照字典序排序
               result.sort()
           
               # 使用\n符号连接所有字符串并返回
               return '\n'.join(result)
           
           
           # 签名主算法
           def sign(credentials, http_method, path, headers, params,
                    timestamp=0, expiration_in_seconds=1800, headers_to_sign=None):
               headers = headers or {}
               params = params or {}
           
               # 1.生成sign key
               # 1.1.生成auth-string,格式为:bce-auth-v1/{accessKeyId}/{timestamp}/{expirationPeriodInSeconds}
               sign_key_info = 'bce-auth-v1/%s/%s/%d' % (
                   credentials.access_key_id,
                   get_canonical_time(timestamp),
                   expiration_in_seconds)
               # 1.2.使用auth-string加上SK,用SHA-256生成sign key
               sign_key = hmac.new(
                   credentials.secret_access_key,
                   sign_key_info,
                   hashlib.sha256).hexdigest()
           
               # 2.生成规范化uri
               canonical_uri = get_canonical_uri(path)
           
               # 3.生成规范化query string
               canonical_querystring = get_canonical_querystring(params)
           
               # 4.生成规范化header
               canonical_headers = get_canonical_headers(headers, headers_to_sign)
           
               # 5.使用'\n'将HTTP METHOD和2、3、4中的结果连接起来,成为一个大字符串
               string_to_sign = '\n'.join(
                   [http_method, canonical_uri, canonical_querystring, canonical_headers])
           
               # 6.使用5中生成的签名串和1中生成的sign key,用SHA-256算法生成签名结果
               sign_result = hmac.new(sign_key, string_to_sign, hashlib.sha256).hexdigest()
           
               # 7.拼接最终签名结果串
               if headers_to_sign:
                   # 指定header to sign
                   result = '%s/%s/%s' % (sign_key_info, ';'.join(headers_to_sign), sign_result)
               else:
                   # 不指定header to sign情况下的默认签名结果串
                   result = '%s//%s' % (sign_key_info, sign_result)
           
               return result
           
           if __name__ == "__main__":
               credentials = BceCredentials("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb")
               http_method = "PUT"
               path = "/test/myfolder/readme.txt"
               headers = {"host": "bj.bcebos.com",
                          "content-length": 8,
                          "content-md5": "NFzcPqhviddjRNnSOGo4rw==",
                          "content-type":"text/plain",
                          "x-bce-date": "2015-04-27T08:23:49Z"}
               params = {"partNumber": 9,
                         "uploadId": "a44cc9bab11cbd156984767aad637851"}
               timestamp = 1430123029
               result = sign(credentials, http_method, path, headers, params, timestamp)
               print result

          Php示例

          假设用户向北京的BOS集群使用UploadPart接口上传一个文件的最后一个Part,内容为Example

          • Bucket name:test
          • Object key:myfolder/readme.txt
          • uploadId:a44cc9bab11cbd156984767aad637851
          • partNumber:9
          • Access Key ID:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
          • Secret Access Key:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
          • 时间:北京时间2015年4月27日16点23分49秒(转换为UTC时间是2015年4月27日8点23分49秒)

          则其HTTP请求如下:

          PUT /test/myfolder/readme.txt?partNumber=9&uploadId=a44cc9bab11cbd156984767aad637851 HTTP/1.1
          Host: bj.bcebos.com
          Date: Mon, 27 Apr 2015 16:23:49 +0800
          Content-Type: text/plain
          Content-Length: 8
          Content-Md5: NFzcPqhviddjRNnSOGo4rw==
          x-bce-date: 2015-04-27T08:23:49Z
          
          Example

          签名示范代码

          $signer = new SampleSigner();
          $credentials = array("ak" => "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","sk" => "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb");
          $httpMethod = "PUT";
          $path = "/v1/test/myfolder/readme.txt";
          $headers = array("Host" => "bj.bcebos.com",
                          "Content-Length" => 8,
                          "Content-MD5" => "NFzcPqhviddjRNnSOGo4rw==",
                          "Content-Type" => "text/plain",
                          "x-bce-date" => "2015-04-27T08:23:49Z");
          $params = array("partNumber" => 9, "uploadId" => "a44cc9bab11cbd156984767aad637851");
          $timestamp = new \DateTime();
          $timestamp->setTimestamp(1430123029);
          $options = array(SignOption::TIMESTAMP => $timestamp);
          $ret = $signer->sign($credentials, $httpMethod, $path, $headers, $params, $options);
          print $ret;

          请点击此处获取完整代码

          完整代码内容如下:

          <?php
          /*
          * Copyright (c) 2014 Baidu.com, Inc. All Rights Reserved
          *
          * Licensed under the Apache License, Version 2.0 (the "License"); you may not
          * use this file except in compliance with the License. You may obtain a copy of
          * the License at
          *
          * Http://www.apache.org/licenses/LICENSE-2.0
          *
          * Unless required by applicable law or agreed to in writing, software
          * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
          * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
          * License for the specific language governing permissions and limitations under
          * the License.
          */
          
          namespace BaiduBce\Auth;
          
          class SignOption
          {
              const EXPIRATION_IN_SECONDS = 'expirationInSeconds';
          
              const HEADERS_TO_SIGN = 'headersToSign';
          
              const TIMESTAMP = 'timestamp';
          
              const DEFAULT_EXPIRATION_IN_SECONDS = 1800;
          
              const MIN_EXPIRATION_IN_SECONDS = 300;
          
              const MAX_EXPIRATION_IN_SECONDS = 129600;
          }
          
          class HttpUtil
          {
              // 根据RFC 3986,除了:
              //   1.大小写英文字符
              //   2.阿拉伯数字
              //   3.点'.'、波浪线'~'、减号'-'以及下划线'_'
              // 以外都要编码
              public static $PERCENT_ENCODED_STRINGS;
          
              //填充编码数组
              public static function __init()
              {
                  HttpUtil::$PERCENT_ENCODED_STRINGS = array();
                  for ($i = 0; $i < 256; ++$i) {
                      HttpUtil::$PERCENT_ENCODED_STRINGS[$i] = sprintf("%%%02X", $i);
                  }
          
                  //a-z不编码
                  foreach (range('a', 'z') as $ch) {
                      HttpUtil::$PERCENT_ENCODED_STRINGS[ord($ch)] = $ch;
                  }
          
                  //A-Z不编码
                  foreach (range('A', 'Z') as $ch) {
                      HttpUtil::$PERCENT_ENCODED_STRINGS[ord($ch)] = $ch;
                  }
          
                  //0-9不编码
                  foreach (range('0', '9') as $ch) {
                      HttpUtil::$PERCENT_ENCODED_STRINGS[ord($ch)] = $ch;
                  }
          
                  //以下4个字符不编码
                  HttpUtil::$PERCENT_ENCODED_STRINGS[ord('-')] = '-';
                  HttpUtil::$PERCENT_ENCODED_STRINGS[ord('.')] = '.';
                  HttpUtil::$PERCENT_ENCODED_STRINGS[ord('_')] = '_';
                  HttpUtil::$PERCENT_ENCODED_STRINGS[ord('~')] = '~';
              }
          
              //在uri编码中不能对'/'编码
              public static function urlEncodeExceptSlash($path)
              {
                  return str_replace("%2F", "/", HttpUtil::urlEncode($path));
              }
          
              //使用编码数组编码
              public static function urlEncode($value)
              {
                  $result = '';
                  for ($i = 0; $i < strlen($value); ++$i) {
                      $result .= HttpUtil::$PERCENT_ENCODED_STRINGS[ord($value[$i])];
                  }
                  return $result;
              }
          
              //生成标准化QueryString
              public static function getCanonicalQueryString(array $parameters)
              {
                  //没有参数,直接返回空串
                  if (count($parameters) == 0) {
                      return '';
                  }
          
                  $parameterStrings = array();
                  foreach ($parameters as $k => $v) {
                      //跳过Authorization字段
                      if (strcasecmp('Authorization', $k) == 0) {
                          continue;
                      }
                      if (!isset($k)) {
                          throw new \InvalidArgumentException(
                              "parameter key should not be null"
                          );
                      }
                      if (isset($v)) {
                          //对于有值的,编码后放在=号两边
                          $parameterStrings[] = HttpUtil::urlEncode($k)
                              . '=' . HttpUtil::urlEncode((string) $v);
                      } else {
                          //对于没有值的,只将key编码后放在=号的左边,右边留空
                          $parameterStrings[] = HttpUtil::urlEncode($k) . '=';
                      }
                  }
                  //按照字典序排序
                  sort($parameterStrings);
          
                  //使用'&'符号连接它们
                  return implode('&', $parameterStrings);
              }
          
              //生成标准化uri
              public static function getCanonicalURIPath($path)
              {
                  //空路径设置为'/'
                  if (empty($path)) {
                      return '/';
                  } else {
                      //所有的uri必须以'/'开头
                      if ($path[0] == '/') {
                          return HttpUtil::urlEncodeExceptSlash($path);
                      } else {
                          return '/' . HttpUtil::urlEncodeExceptSlash($path);
                      }
                  }
              }
          
              //生成标准化http请求头串
              public static function getCanonicalHeaders($headers)
              {
                  //如果没有headers,则返回空串
                  if (count($headers) == 0) {
                      return '';
                  }
          
                  $headerStrings = array();
                  foreach ($headers as $k => $v) {
                      //跳过key为null的
                      if ($k === null) {
                          continue;
                      }
                      //如果value为null,则赋值为空串
                      if ($v === null) {
                          $v = '';
                      }
                      //trim后再encode,之后使用':'号连接起来
                      $headerStrings[] = HttpUtil::urlEncode(strtolower(trim($k))) . ':' . HttpUtil::urlEncode(trim($v));
                  }
                  //字典序排序
                  sort($headerStrings);
          
                  //用'\n'把它们连接起来
                  return implode("\n", $headerStrings);
              }
          
          }
          HttpUtil::__init();
          
          
          class SampleSigner
          {
          
              const BCE_AUTH_VERSION = "bce-auth-v1";
              const BCE_PREFIX = 'x-bce-';
          
              //不指定headersToSign情况下,默认签名http头,包括:
              //    1.host
              //    2.content-length
              //    3.content-type
              //    4.content-md5
              public static $defaultHeadersToSign;
          
              public static function  __init()
              {
                  SampleSigner::$defaultHeadersToSign = array(
                      "host",
                      "content-length",
                      "content-type",
                      "content-md5",
                  );
              }
          
              //签名函数
              public function sign(
                  array $credentials,
                  $httpMethod,
                  $path,
                  $headers,
                  $params,
                  $options = array()
              ) {
                  //设定签名有效时间
                  if (!isset($options[SignOption::EXPIRATION_IN_SECONDS])) {
                      //默认值1800秒
                      $expirationInSeconds = SignOption::DEFAULT_EXPIRATION_IN_SECONDS;
                  } else {
                      $expirationInSeconds = $options[SignOption::EXPIRATION_IN_SECONDS];
                  }
          
                  //解析ak sk
                  $accessKeyId = $credentials['ak'];
                  $secretAccessKey = $credentials['sk'];
          
                  //设定时间戳,注意:如果自行指定时间戳需要为UTC时间
                  if (!isset($options[SignOption::TIMESTAMP])) {
                      //默认值当前时间
                      $timestamp = new \DateTime();
                  } else {
                      $timestamp = $options[SignOption::TIMESTAMP];
                  }
                  $timestamp->setTimezone(new \DateTimeZone("UTC"));
          
                  //生成authString
                  $authString = SampleSigner::BCE_AUTH_VERSION . '/' . $accessKeyId . '/'
                      . $timestamp->format("Y-m-d\TH:i:s\Z") . '/' . $expirationInSeconds;
          
                  //使用sk和authString生成signKey
                  $signingKey = hash_hmac('sha256', $authString, $secretAccessKey);
          
                  //生成标准化URI
                  $canonicalURI = HttpUtil::getCanonicalURIPath($path);
          
                  //生成标准化QueryString
                  $canonicalQueryString = HttpUtil::getCanonicalQueryString($params);
          
                  //填充headersToSign,也就是指明哪些header参与签名
                  $headersToSignOption = null;
                  if (isset($options[SignOption::HEADERS_TO_SIGN])) {
                      $headersToSignOption = $options[SignOption::HEADERS_TO_SIGN];
                  }
                  
                  $headersToSign = SampleSigner::getHeadersToSign($headers, $headersToSignOption);
                  
                  //生成标准化header
                  $canonicalHeader = HttpUtil::getCanonicalHeaders($headersToSign);
                  
                  $headersToSign = array_keys($headersToSign);
                  sort($headersToSign);
                  //整理headersToSign,以';'号连接
                  $signedHeaders = '';
                  if ($headersToSignOption !== null) {
                      $signedHeaders = strtolower(
                          trim(implode(";", $headersToSign))
                      );
                  }
          
                  //组成标准请求串
                  $canonicalRequest = "$httpMethod\n$canonicalURI\n"
                      . "$canonicalQueryString\n$canonicalHeader";
          
                  //使用signKey和标准请求串完成签名
                  $signature = hash_hmac('sha256', $canonicalRequest, $signingKey);
          
                  //组成最终签名串
                  $authorizationHeader = "$authString/$signedHeaders/$signature";
          
                  return $authorizationHeader;
              }
          
              /** 根据headsToSign过滤应该参与签名的header
               * 
               * @param $headers array
               * @param $headersToSign array
               * @return array
               */
              public static function getHeadersToSign($headers, $headersToSign)
              {
          
                  $ret = array();
                  if ($headersToSign !== null) {
                      $tmp = array();
          
                      //处理headers的key:去掉前后的空白并转化成小写
                      foreach ($headersToSign as $header) {
                          $tmp[] = strtolower(trim($header));
                      }
                      $headersToSign = $tmp;
                  }
                  foreach ($headers as $k => $v) {
                      if (trim((string) $v) !== '') {
                          if ($headersToSign !== null) {
                              //预处理headersToSign:去掉前后的空白并转化成小写
                              if (in_array(strtolower(trim($k)), $headersToSign)) {
                                  $ret[$k] = $v;
                              }
                          } else {
                              //如果没有headersToSign,则根据默认规则来选取headers
                              if (SampleSigner::isDefaultHeaderToSign($k, $headersToSign)) {
                                  $ret[$k] = $v;
                              }
                          }
                      }
                  }
                  return $ret;
              }
          
              /**
               * 检查header是不是默认参加签名的:
               * 1.是host、content-type、content-md5、content-length之一
               * 2.以x-bce开头
               *
               * @param $header string
               * @return bool
               */
              public static function isDefaultHeaderToSign($header)
              {
                  $header = strtolower(trim($header));
                  if (in_array($header, SampleSigner::$defaultHeadersToSign)) {
                      return true;
                  }
                  $prefix = substr($header, 0, strlen(SampleSigner::BCE_PREFIX));
                  if ($prefix === SampleSigner::BCE_PREFIX) {
                      return true;
                  } else {
                      return false;
                  }
              }
          }
          
          SampleSigner::__init();
          
          
          
          //签名示范代码
          $signer = new SampleSigner();
          $credentials = array("ak" => "0b0f67dfb88244b289b72b142befad0c","sk" => "bad522c2126a4618a8125f4b6cf6356f");
          $httpMethod = "PUT";
          $path = "/v1/test/myfolder/readme.txt";
          $headers = array("Host" => "bj.bcebos.com",
                          "Content-Length" => 8,
                          "Content-MD5" => "0a52730597fb4ffa01fc117d9e71e3a9",
                          "Content-Type" => "text/plain",
                          "x-bce-date" => "2015-04-27T08:23:49Z");
          $params = array("partNumber" => 9, "uploadId" => "VXBsb2FkIElpZS5tMnRzIHVwbG9hZA");
          date_default_timezone_set("PRC");
          $timestamp = new \DateTime();
          $timestamp->setTimestamp(1430123029);
          $options = array(SignOption::TIMESTAMP => $timestamp);
          // $options = array(SignOption::TIMESTAMP => $timestamp, SignOption::HEADERS_TO_SIGN => array("Content-Type", "Host", "x-bce-date"));
          $ret = $signer->sign($credentials, $httpMethod, $path, $headers, $params, $options);
          print $ret;

          Java示例

          用户可参考以下代码,进一步了解百度智能云API认证机制。

          代码下载路径:https://github.com/baidubce/bce-sdk-java/blob/master/src/main/java/com/baidubce/auth/BceV1Signer.java

          说明:Android语言的API认证示例代码和Java示例代码一致。

          /*
           * Copyright (c) 2014 Baidu.com, Inc. All Rights Reserved
           *
           * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
           * the License. You may obtain a copy of the License at
           *
           * http://www.apache.org/licenses/LICENSE-2.0
           *
           * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
           * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
           * specific language governing permissions and limitations under the License.
           */
          
          package com.baidubce.auth;
          
          import com.baidubce.BceClientException;
          import com.baidubce.http.Headers;
          import com.baidubce.internal.InternalRequest;
          import com.baidubce.util.DateUtils;
          import com.baidubce.util.HttpUtils;
          import com.google.common.base.Joiner;
          import com.google.common.collect.Lists;
          import com.google.common.collect.Maps;
          import com.google.common.collect.Sets;
          
          import org.apache.commons.codec.binary.Hex;
          import org.slf4j.Logger;
          import org.slf4j.LoggerFactory;
          
          import javax.crypto.Mac;
          import javax.crypto.spec.SecretKeySpec;
          
          import java.nio.charset.Charset;
          import java.util.Collections;
          import java.util.Date;
          import java.util.List;
          import java.util.Map;
          import java.util.Set;
          import java.util.SortedMap;
          
          import static com.google.common.base.Preconditions.checkNotNull;
          
          /**
           * The V1 implementation of Signer with the BCE signing protocol.
           */
          public class BceV1Signer implements Signer {
          
              private static final Logger logger = LoggerFactory.getLogger(BceV1Signer.class);
          
              private static final String BCE_AUTH_VERSION = "bce-auth-v1";
              private static final String DEFAULT_ENCODING = "UTF-8";
              private static final Charset UTF8 = Charset.forName(DEFAULT_ENCODING);
          
              // Default headers to sign with the BCE signing protocol.
              private static final Set<String> defaultHeadersToSign = Sets.newHashSet();
              private static final Joiner headerJoiner = Joiner.on('\n');
              private static final Joiner signedHeaderStringJoiner = Joiner.on(';');
          
              static {
                  BceV1Signer.defaultHeadersToSign.add(Headers.HOST.toLowerCase());
                  BceV1Signer.defaultHeadersToSign.add(Headers.CONTENT_LENGTH.toLowerCase());
                  BceV1Signer.defaultHeadersToSign.add(Headers.CONTENT_TYPE.toLowerCase());
                  BceV1Signer.defaultHeadersToSign.add(Headers.CONTENT_MD5.toLowerCase());
              }
          
              /**
               * @see com.baidubce.auth.Signer#sign(InternalRequest, BceCredentials)
               */
              @Override
              public void sign(InternalRequest request, BceCredentials credentials) {
                  this.sign(request, credentials, null);
              }
          
              /**
               * Sign the given request with the given set of credentials. Modifies the passed-in request to apply the signature.
               *
               * @param request     the request to sign.
               * @param credentials the credentials to sign the request with.
               * @param options     the options for signing.
               */
              @Override
              public void sign(InternalRequest request, BceCredentials credentials, SignOptions options) {
                  checkNotNull(request, "request should not be null.");
          
                  if (credentials == null) {
                      return;
                  }
          
                  if (options == null) {
                      if (request.getSignOptions() != null) {
                          options = request.getSignOptions();
                      } else {
                          options = SignOptions.DEFAULT;
                      }
                  }
          
                  String accessKeyId = credentials.getAccessKeyId();
                  String secretAccessKey = credentials.getSecretKey();
          
                  request.addHeader(Headers.HOST, HttpUtils.generateHostHeader(request.getUri()));
          
                  Date timestamp = options.getTimestamp();
                  if (timestamp == null) {
                      timestamp = new Date();
                  }
          
                  String authString =
                          BceV1Signer.BCE_AUTH_VERSION + "/" + accessKeyId + "/"
                                  + DateUtils.formatAlternateIso8601Date(timestamp) + "/" + options.getExpirationInSeconds();
          
                  String signingKey = this.sha256Hex(secretAccessKey, authString);
                  // Formatting the URL with signing protocol.
                  String canonicalURI = this.getCanonicalURIPath(request.getUri().getPath());
                  // Formatting the query string with signing protocol.
                  String canonicalQueryString = HttpUtils.getCanonicalQueryString(request.getParameters(), true);
                  // Sorted the headers should be signed from the request.
                  SortedMap<String, String> headersToSign =
                          this.getHeadersToSign(request.getHeaders(), options.getHeadersToSign());
                  // Formatting the headers from the request based on signing protocol.
                  String canonicalHeader = this.getCanonicalHeaders(headersToSign);
                  String signedHeaders = "";
                  if (options.getHeadersToSign() != null) {
                      signedHeaders = BceV1Signer.signedHeaderStringJoiner.join(headersToSign.keySet());
                      signedHeaders = signedHeaders.trim().toLowerCase();
                  }
          
                  String canonicalRequest =
                          request.getHttpMethod() + "\n" + canonicalURI + "\n" + canonicalQueryString + "\n" + canonicalHeader;
          
                  // Signing the canonical request using key with sha-256 algorithm.
                  String signature = this.sha256Hex(signingKey, canonicalRequest);
          
                  String authorizationHeader = authString + "/" + signedHeaders + "/" + signature;
          
                  logger.debug("CanonicalRequest:{}\tAuthorization:{}", canonicalRequest.replace("\n", "[\\n]"),
                          authorizationHeader);
          
                  request.addHeader(Headers.AUTHORIZATION, authorizationHeader);
              }
          
              private String getCanonicalURIPath(String path) {
                  if (path == null) {
                      return "/";
                  } else if (path.startsWith("/")) {
                      return HttpUtils.normalizePath(path);
                  } else {
                      return "/" + HttpUtils.normalizePath(path);
                  }
              }
          
              private String getCanonicalHeaders(SortedMap<String, String> headers) {
                  if (headers.isEmpty()) {
                      return "";
                  }
          
                  List<String> headerStrings = Lists.newArrayList();
                  for (Map.Entry<String, String> entry : headers.entrySet()) {
                      String key = entry.getKey();
                      if (key == null) {
                          continue;
                      }
                      String value = entry.getValue();
                      if (value == null) {
                          value = "";
                      }
                      headerStrings.add(HttpUtils.normalize(key.trim().toLowerCase()) + ':' + HttpUtils.normalize(value.trim()));
                  }
                  Collections.sort(headerStrings);
          
                  return headerJoiner.join(headerStrings);
              }
          
              private SortedMap<String, String> getHeadersToSign(Map<String, String> headers, Set<String> headersToSign) {
                  SortedMap<String, String> ret = Maps.newTreeMap();
                  if (headersToSign != null) {
                      Set<String> tempSet = Sets.newHashSet();
                      for (String header : headersToSign) {
                          tempSet.add(header.trim().toLowerCase());
                      }
                      headersToSign = tempSet;
                  }
                  for (Map.Entry<String, String> entry : headers.entrySet()) {
                      String key = entry.getKey();
                      if (entry.getValue() != null && !entry.getValue().isEmpty()) {
                          if ((headersToSign == null && this.isDefaultHeaderToSign(key))
                                  || (headersToSign != null && headersToSign.contains(key.toLowerCase())
                                          && !Headers.AUTHORIZATION.equalsIgnoreCase(key))) {
                              ret.put(key, entry.getValue());
                          }
                      }
                  }
                  return ret;
              }
          
              private boolean isDefaultHeaderToSign(String header) {
                  header = header.trim().toLowerCase();
                  return header.startsWith(Headers.BCE_PREFIX) || defaultHeadersToSign.contains(header);
              }
          
              private String sha256Hex(String signingKey, String stringToSign) {
                  try {
                      Mac mac = Mac.getInstance("HmacSHA256");
                      mac.init(new SecretKeySpec(signingKey.getBytes(UTF8), "HmacSHA256"));
                      return new String(Hex.encodeHex(mac.doFinal(stringToSign.getBytes(UTF8))));
                  } catch (Exception e) {
                      throw new BceClientException("Fail to generate the signature", e);
                  }
              }
          
          }

          Javascript示例

          用户可参考以下代码,进一步了解Javascript语言的百度智能云API认证机制。

          代码下载路径:https://github.com/baidubce/bce-sdk-js/blob/master/test/sdk/auth.spec.js

          var Auth = require('@baiducloud/sdk').Auth;
          
          
              var auth = new Auth('my_ak', 'my_sk');
          
              var method = 'PUT';
              var uri = '/v1/bucket/object1';
              var params = {
                  A: null,
                  b: '',
                  C: 'd'
              };
              var headers = {
                  'Host': 'bce.baidu.com',
                  'abc': '123',
                  'x-bce-meta-key1': 'ABC'
              };
          
              var signature = auth.generateAuthorization(method, uri, params, headers, 1402639056);
              expect(signature).to.eql('bce-auth-v1/my_ak/2014-06-13T05:57:36Z/1800/host;x-bce-meta-key1/'
                                       + '80c9672aca2ea9af4bb40b9a8ff458d72df94e97d550840727f3a929af271d25');
          
              signature = auth.generateAuthorization(method, uri, params, headers, 1402639056, 1800);
              expect(signature).to.eql('bce-auth-v1/my_ak/2014-06-13T05:57:36Z/1800/host;'
                                        + 'x-bce-meta-key1/80c9672aca2ea9af4bb40b9a8ff458d72'
                                        + 'df94e97d550840727f3a929af271d25');
          
              method = 'DELETE';
              uri = '/v1/test-bucket1361199862';
              params = {};
              headers = {
                  'Content-Type': 'application/json; charset=utf-8',
                  'Content-Length': 0,
                  'User-Agent': 'This is the user-agent'
              };
              signature = auth.generateAuthorization(method, uri, params, headers, 1402639056, 1800);
              expect(signature).to.eql('bce-auth-v1/my_ak/2014-06-13T05:57:36Z/1800/'
                                        + 'content-length;content-type/'
                                        + 'c9386b15d585960ae5e6972f73ed92a9a682dc81025480ba5b41206d3e489822');

          C#示例

          用户可参考以下代码,进一步了解C#语言的百度智能云API认证机制。完整示例代码:

          using BaiduBce;
          using BaiduBce.Auth;
          using BaiduBce.Services.Bos;
          using BaiduBce.Services.Bos.Model;
          using BaiduBce.Services.Sts;
          using System;
          using System.Collections.Generic;
          using System.Collections.Specialized;
          using System.Diagnostics;
          using System.Globalization;
          using System.IO;
          using System.Net;
          using System.Security.Cryptography;
          using System.Text;
          using System.Web;
          
          namespace BOSTest
          {
              class Program
              {
                  static string UriEncode(string input, bool encodeSlash = false)
                  {
                      StringBuilder builder = new StringBuilder();
                      foreach (byte b in Encoding.UTF8.GetBytes(input))
                      {
                          if ((b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z') || (b >= '0' && b <= '9') || b == '_' || b == '-' || b == '~' || b == '.')
                          {
                              builder.Append((char)b);
                          }
                          else if (b == '/')
                          {
                              if (encodeSlash)
                              {
                                  builder.Append("%2F");
                              }
                              else
                              {
                                  builder.Append((char)b);
                              }
                          }
                          else
                          {
                              builder.Append('%').Append(b.ToString("X2"));
                          }
                      }
                      return builder.ToString();
                  }
          
                  static string Hex(byte[] data)
                  {
                      var sb = new StringBuilder();
                      foreach (var b in data)
                      {
                          sb.Append(b.ToString("x2"));
                      }
                      return sb.ToString();
                  }
          
                  static string CanonicalRequest(HttpWebRequest req)
                  {
                      Uri uri = req.RequestUri;
                      StringBuilder canonicalReq = new StringBuilder();
                      canonicalReq.Append(req.Method).Append("\n").Append(UriEncode(Uri.UnescapeDataString(uri.AbsolutePath))).Append("\n");
          
                      var parameters = HttpUtility.ParseQueryString(uri.Query);
                      List<string> parameterStrings = new List<string>();
                      foreach (KeyValuePair<string, string> entry in parameters)
                      {
                          parameterStrings.Add(UriEncode(entry.Key) + '=' + UriEncode(entry.Value));
                      }
                      parameterStrings.Sort();
                      canonicalReq.Append(string.Join("&", parameterStrings.ToArray())).Append("\n");
          
                      string host = uri.Host;
                      if (!(uri.Scheme == "https" && uri.Port == 443) && !(uri.Scheme == "http" && uri.Port == 80))
                      {
                          host += ":" + uri.Port;
                      }
                      canonicalReq.Append("host:" + UriEncode(host));
                      return canonicalReq.ToString();
                  }
          
                  static void Main(string[] args)
                  {
                      string bucket = "mybucket";
                      string key = "我的文件";
                      string ak = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
                      string sk = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";
                      DateTime now = DateTime.Now;
                      int expirationInSeconds = 1200;
          
                      HttpWebRequest req = WebRequest.Create("http://bj.bcebos.com/" + bucket + "/" + key) as HttpWebRequest;
                      Uri uri = req.RequestUri;
                      req.Method = "GET";
          
                      string signDate = now.ToUniversalTime().ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ssK");
                      Console.WriteLine(signDate);
                      string authString = "bce-auth-v1/" + ak + "/" + signDate + "/" + expirationInSeconds;
                      string signingKey = Hex(new HMACSHA256(Encoding.UTF8.GetBytes(sk)).ComputeHash(Encoding.UTF8.GetBytes(authString)));
                      Console.WriteLine(signingKey);
          
                      string canonicalRequestString = CanonicalRequest(req);
                      Console.WriteLine(canonicalRequestString);
          
                      string signature = Hex(new HMACSHA256(Encoding.UTF8.GetBytes(signingKey)).ComputeHash(Encoding.UTF8.GetBytes(canonicalRequestString)));
                      string authorization = authString + "/host/" + signature;
                      Console.WriteLine(authorization);
          
                      req.Headers.Add("x-bce-date", signDate);
                      req.Headers.Add(HttpRequestHeader.Authorization, authorization);
          
                      HttpWebResponse res;
                      string message = "";
                      try
                      {
                          res = req.GetResponse() as HttpWebResponse;
                      }
                      catch (WebException e)
                      {
                          res = e.Response as HttpWebResponse;
                          message = new StreamReader(res.GetResponseStream()).ReadToEnd();
                      }
                      Console.WriteLine((int)res.StatusCode);
                      Console.WriteLine(res.Headers);
                      Console.WriteLine(message);
                      Console.ReadLine();
                  }
              }
          }

          iOS示例

          用户可参考以下代码,进一步了解iOS的百度智能云API认证机制。代码包含类代码和调用代码两部分,完整代码地址:代码。调用代码请参考:

          #import <XCTest/XCTest.h>
          #import "BDCloudSigner.h"
          
          @interface UT : XCTestCase
          @end
          
          @implementation UT
          
          id<BDCloudSigner> createSigner() {
              BDCloudCredentials* credentials = [BDCloudCredentials new];
              credentials.accessKey = @"<access key>";
              credentials.secretKey = @"<secret key>";
          
              id<BDCloudSigner> signer = [[BDCloudAKSKSigner alloc] initWithCredentials:credentials];
              signer.expiredTimeInSeconds = 3600;
          
              return signer;
          }
          
          NSMutableURLRequest* createRequest() {
              // create url directly, or use NSURLComponents.
              NSURL* url = [NSURL URLWithString:@"http://bj.bcebos.com/v1/bucket/object?append"];
          
              // create request.
              NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:url];
              request.HTTPMethod = @"POST";
              [request setValue:@"<length>" forHTTPHeaderField:@"Content-Length"];
              [request setValue:@"<md5>" forHTTPHeaderField:@"Content-MD5"];
              [request setValue:@"text/plain" forHTTPHeaderField:@"Content-Type"];
          
              // custom metadata key should begin with lower case prefix 'x-bce-'.
              [request setValue:@"2017-01-08T21:42:30Z" forHTTPHeaderField:@"x-bce-user-metadata-createtime"];
          
              // Host will be set when call sign.
              //[request setValue:@"bj.bcebos.com" forHTTPHeaderField:@"Host"];
          
              return request;
          }
          
          void sign() {
              id<BDCloudSigner> signer = createSigner();
              NSMutableURLRequest* request = createRequest();
              if (![signer sign:request]) {
                  return;
              }
          
              // url
              NSURL* fileURL = [NSURL fileURLWithPath:@"<file path>"];
          
              // send request
              // sample purpose, don't care task will running correctly.
              [[NSURLSession sharedSession] uploadTaskWithRequest:request
                                                         fromFile:fileURL];
          }
          
          - (void)testAKSKSigner {
              sign();
          }
          
          @end

          C++示例

          用户可参考以下代码,进一步了解C++的百度智能云API认证机制。完整代码可下载百度智能云C++ sdk参考,下载地址:C++ SDK

          std::string DefaultSigner::generate_auth(HttpRequest *request, int expire_seconds) {
              std::stringstream auth_str;
              std::string sign_time = DateUtil::format_iso8601_date(time(NULL));
              auth_str << "bce-auth-v1/" << _credential.ak() << "/" << sign_time << "/" << expire_seconds;
              std::string sign_key = StringUtil::hmac_sha256_hex(_credential.sk(), auth_str.str());
          
              std::ostringstream canonical_req;
              // method
              canonical_req << method_str(request->method()) << '\n';
              // uri
              canonical_req << StringUtil::url_encode(request->uri(), false) << '\n';
              // query string
              const StringMap &params = request->parameters();
              if (params.size() > 0) {
                  StringMap tmp;
                  for (StringMap::const_iterator it = params.begin(); it != params.end(); ++it) {
                      std::ostringstream p;
                      p << StringUtil::url_encode(StringUtil::trim(it->first)) << '='
                          << StringUtil::url_encode(StringUtil::trim(it->second));
                      tmp[p.str()] = "";
                  }
                  StringMap::iterator it = tmp.begin();
                  canonical_req << it->first;
                  for (++it; it != tmp.end(); ++it) {
                      canonical_req << '&' << it->first;
                  }
              }
              canonical_req << '\n';
          
              bool use_sts = !_credential.sts_token().empty();
              // header
              canonical_req << "host:" << StringUtil::url_encode(request->host());
              if (use_sts) {
                  canonical_req << "\nx-bce-security-token:" << StringUtil::url_encode(_credential.sts_token());
              }
              LOG(DEBUG) << "canonical request: [" << canonical_req.str() << ']';
          
              std::string signature = StringUtil::hmac_sha256_hex(sign_key, canonical_req.str());
              auth_str << "/host";
              if (use_sts) {
                  auth_str << ";x-bce-security-token";
                  request->append_header("x-bce-security-token", _credential.sts_token());
              }
              auth_str << "/" << signature;
              return auth_str.str();
          }
          
          void DefaultSigner::sign(HttpRequest *request) {
              request->append_header("Authorization", generate_auth(request, _sign_expire_seconds));
          }
          上一篇
          在URL中包含认证字符串
          下一篇
          常见签名认证错误排查