Sample-Code
更新时间:2023-02-06
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认证机制。
说明: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'; // 请勿传入已encode的字符串,sdk执行的时候会encode一次
var params = {
A: null,
b: '',
C: 'd'
};
var headers = {
'Host': 'bce.baidu.com',
'abc': '123',
'x-bce-meta-key1': 'ABC'
};
var headersToSign = ['Host', 'abc', 'x-bce-meta-key1'];
var signature = auth.generateAuthorization(method, uri, params, headers, 1402639056, 1800, headersToSign);
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, headersToSign);
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 ¶ms = 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));
}