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请求如下:
XML
1PUT /test/myfolder/readme.txt?partNumber=9&uploadId=a44cc9bab11cbd156984767aad637851 HTTP/1.1
2Host: bj.bcebos.com
3Date: Mon, 27 Apr 2015 16:23:49 +0800
4Content-Type: text/plain
5Content-Length: 8
6Content-Md5: NFzcPqhviddjRNnSOGo4rw==
7x-bce-date: 2015-04-27T08:23:49Z
8
9Example
用户可根据上述HTTP请求填写以下函数中的各个字段。
Plain Text
1 if __name__ == "__main__":
2 credentials = BceCredentials("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb")
3 http_method = "PUT"
4 path = "/test/myfolder/readme.txt"
5 headers = {"host": "bj.bcebos.com",
6 "content-length": 8,
7 "content-md5": "NFzcPqhviddjRNnSOGo4rw==",
8 "content-type":"text/plain",
9 "x-bce-date": "2015-04-27T08:23:49Z"}
10 params = {"partNumber": 9,
11 "uploadId": "a44cc9bab11cbd156984767aad637851"}
12 timestamp = 1430123029
13 result = sign(credentials, http_method, path, headers, params, timestamp)
14 print result
完整代码内容如下:
Plain Text
1 # -*- coding: UTF-8 -*-
2 import hashlib
3 import hmac
4 import string
5 import datetime
6
7
8 AUTHORIZATION = "authorization"
9 BCE_PREFIX = "x-bce-"
10 DEFAULT_ENCODING = 'UTF-8'
11
12
13 # 保存AK/SK的类
14 class BceCredentials(object):
15 def __init__(self, access_key_id, secret_access_key):
16 self.access_key_id = access_key_id
17 self.secret_access_key = secret_access_key
18
19
20 # 根据RFC 3986,除了:
21 # 1.大小写英文字符
22 # 2.阿拉伯数字
23 # 3.点'.'、波浪线'~'、减号'-'以及下划线'_'
24 # 以外都要编码
25 RESERVED_CHAR_SET = set(string.ascii_letters + string.digits + '.~-_')
26 def get_normalized_char(i):
27 char = chr(i)
28 if char in RESERVED_CHAR_SET:
29 return char
30 else:
31 return '%%%02X' % i
32 NORMALIZED_CHAR_LIST = [get_normalized_char(i) for i in range(256)]
33
34
35 # 正规化字符串
36 def normalize_string(in_str, encoding_slash=True):
37 if in_str is None:
38 return ''
39
40 # 如果输入是unicode,则先使用UTF8编码之后再编码
41 in_str = in_str.encode(DEFAULT_ENCODING) if isinstance(in_str, unicode) else str(in_str)
42
43 # 在生成规范URI时。不需要对斜杠'/'进行编码,其他情况下都需要
44 if encoding_slash:
45 encode_f = lambda c: NORMALIZED_CHAR_LIST[ord(c)]
46 else:
47 # 仅仅在生成规范URI时。不需要对斜杠'/'进行编码
48 encode_f = lambda c: NORMALIZED_CHAR_LIST[ord(c)] if c != '/' else c
49
50 # 按照RFC 3986进行编码
51 return ''.join([encode_f(ch) for ch in in_str])
52
53
54 # 生成规范时间戳
55 def get_canonical_time(timestamp=0):
56 # 不使用任何参数调用的时候返回当前时间
57 if timestamp == 0:
58 utctime = datetime.datetime.utcnow()
59 else:
60 utctime = datetime.datetime.utcfromtimestamp(timestamp)
61
62 # 时间戳格式:[year]-[month]-[day]T[hour]:[minute]:[second]Z
63 return "%04d-%02d-%02dT%02d:%02d:%02dZ" % (
64 utctime.year, utctime.month, utctime.day,
65 utctime.hour, utctime.minute, utctime.second)
66
67
68 # 生成规范URI
69 def get_canonical_uri(path):
70 # 规范化URI的格式为:/{bucket}/{object},并且要对除了斜杠"/"之外的所有字符编码
71 return normalize_string(path, False)
72
73
74 # 生成规范query string
75 def get_canonical_querystring(params):
76 if params is None:
77 return ''
78
79 # 除了authorization之外,所有的query string全部加入编码
80 result = ['%s=%s' % (normalize_string(k), normalize_string(v)) for k, v in params.items() if k.lower != AUTHORIZATION]
81
82 # 按字典序排序
83 result.sort()
84
85 # 使用&符号连接所有字符串并返回
86 return '&'.join(result)
87
88
89 # 生成规范header
90 def get_canonical_headers(headers, headers_to_sign=None):
91 headers = headers or {}
92
93 # 没有指定header_to_sign的情况下,默认使用:
94 # 1.host
95 # 2.content-md5
96 # 3.content-length
97 # 4.content-type
98 # 5.所有以x-bce-开头的header项
99 # 生成规范header
100 if headers_to_sign is None or len(headers_to_sign) == 0:
101 headers_to_sign = {"host", "content-md5", "content-length", "content-type"}
102
103 # 对于header中的key,去掉前后的空白之后需要转化为小写
104 # 对于header中的value,转化为str之后去掉前后的空白
105 f = lambda (key, value): (key.strip().lower(), str(value).strip())
106
107 result = []
108 for k, v in map(f, headers.iteritems()):
109 # 无论何种情况,以x-bce-开头的header项都需要被添加到规范header中
110 if k.startswith(BCE_PREFIX) or k in headers_to_sign:
111 result.append("%s:%s" % (normalize_string(k), normalize_string(v)))
112
113 # 按照字典序排序
114 result.sort()
115
116 # 使用\n符号连接所有字符串并返回
117 return '\n'.join(result)
118
119
120 # 签名主算法
121 def sign(credentials, http_method, path, headers, params,
122 timestamp=0, expiration_in_seconds=1800, headers_to_sign=None):
123 headers = headers or {}
124 params = params or {}
125
126 # 1.生成sign key
127 # 1.1.生成auth-string,格式为:bce-auth-v1/{accessKeyId}/{timestamp}/{expirationPeriodInSeconds}
128 sign_key_info = 'bce-auth-v1/%s/%s/%d' % (
129 credentials.access_key_id,
130 get_canonical_time(timestamp),
131 expiration_in_seconds)
132 # 1.2.使用auth-string加上SK,用SHA-256生成sign key
133 sign_key = hmac.new(
134 credentials.secret_access_key,
135 sign_key_info,
136 hashlib.sha256).hexdigest()
137
138 # 2.生成规范化uri
139 canonical_uri = get_canonical_uri(path)
140
141 # 3.生成规范化query string
142 canonical_querystring = get_canonical_querystring(params)
143
144 # 4.生成规范化header
145 canonical_headers = get_canonical_headers(headers, headers_to_sign)
146
147 # 5.使用'\n'将HTTP METHOD和2、3、4中的结果连接起来,成为一个大字符串
148 string_to_sign = '\n'.join(
149 [http_method, canonical_uri, canonical_querystring, canonical_headers])
150
151 # 6.使用5中生成的签名串和1中生成的sign key,用SHA-256算法生成签名结果
152 sign_result = hmac.new(sign_key, string_to_sign, hashlib.sha256).hexdigest()
153
154 # 7.拼接最终签名结果串
155 if headers_to_sign:
156 # 指定header to sign
157 result = '%s/%s/%s' % (sign_key_info, ';'.join(headers_to_sign), sign_result)
158 else:
159 # 不指定header to sign情况下的默认签名结果串
160 result = '%s//%s' % (sign_key_info, sign_result)
161
162 return result
163
164 if __name__ == "__main__":
165 credentials = BceCredentials("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb")
166 http_method = "PUT"
167 path = "/test/myfolder/readme.txt"
168 headers = {"host": "bj.bcebos.com",
169 "content-length": 8,
170 "content-md5": "NFzcPqhviddjRNnSOGo4rw==",
171 "content-type":"text/plain",
172 "x-bce-date": "2015-04-27T08:23:49Z"}
173 params = {"partNumber": 9,
174 "uploadId": "a44cc9bab11cbd156984767aad637851"}
175 timestamp = 1430123029
176 result = sign(credentials, http_method, path, headers, params, timestamp)
177 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请求如下:
XML
1PUT /test/myfolder/readme.txt?partNumber=9&uploadId=a44cc9bab11cbd156984767aad637851 HTTP/1.1
2Host: bj.bcebos.com
3Date: Mon, 27 Apr 2015 16:23:49 +0800
4Content-Type: text/plain
5Content-Length: 8
6Content-Md5: NFzcPqhviddjRNnSOGo4rw==
7x-bce-date: 2015-04-27T08:23:49Z
8
9Example
签名示范代码
Plain Text
1$signer = new SampleSigner();
2$credentials = array("ak" => "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","sk" => "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb");
3$httpMethod = "PUT";
4$path = "/v1/test/myfolder/readme.txt";
5$headers = array("Host" => "bj.bcebos.com",
6 "Content-Length" => 8,
7 "Content-MD5" => "NFzcPqhviddjRNnSOGo4rw==",
8 "Content-Type" => "text/plain",
9 "x-bce-date" => "2015-04-27T08:23:49Z");
10$params = array("partNumber" => 9, "uploadId" => "a44cc9bab11cbd156984767aad637851");
11$timestamp = new \DateTime();
12$timestamp->setTimestamp(1430123029);
13$options = array(SignOption::TIMESTAMP => $timestamp);
14$ret = $signer->sign($credentials, $httpMethod, $path, $headers, $params, $options);
15print $ret;
完整代码内容如下:
Plain Text
1<?php
2/*
3* Copyright (c) 2014 Baidu.com, Inc. All Rights Reserved
4*
5* Licensed under the Apache License, Version 2.0 (the "License"); you may not
6* use this file except in compliance with the License. You may obtain a copy of
7* the License at
8*
9* Http://www.apache.org/licenses/LICENSE-2.0
10*
11* Unless required by applicable law or agreed to in writing, software
12* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14* License for the specific language governing permissions and limitations under
15* the License.
16*/
17
18namespace BaiduBce\Auth;
19
20class SignOption
21{
22 const EXPIRATION_IN_SECONDS = 'expirationInSeconds';
23
24 const HEADERS_TO_SIGN = 'headersToSign';
25
26 const TIMESTAMP = 'timestamp';
27
28 const DEFAULT_EXPIRATION_IN_SECONDS = 1800;
29
30 const MIN_EXPIRATION_IN_SECONDS = 300;
31
32 const MAX_EXPIRATION_IN_SECONDS = 129600;
33}
34
35class HttpUtil
36{
37 // 根据RFC 3986,除了:
38 // 1.大小写英文字符
39 // 2.阿拉伯数字
40 // 3.点'.'、波浪线'~'、减号'-'以及下划线'_'
41 // 以外都要编码
42 public static $PERCENT_ENCODED_STRINGS;
43
44 //填充编码数组
45 public static function __init()
46 {
47 HttpUtil::$PERCENT_ENCODED_STRINGS = array();
48 for ($i = 0; $i < 256; ++$i) {
49 HttpUtil::$PERCENT_ENCODED_STRINGS[$i] = sprintf("%%%02X", $i);
50 }
51
52 //a-z不编码
53 foreach (range('a', 'z') as $ch) {
54 HttpUtil::$PERCENT_ENCODED_STRINGS[ord($ch)] = $ch;
55 }
56
57 //A-Z不编码
58 foreach (range('A', 'Z') as $ch) {
59 HttpUtil::$PERCENT_ENCODED_STRINGS[ord($ch)] = $ch;
60 }
61
62 //0-9不编码
63 foreach (range('0', '9') as $ch) {
64 HttpUtil::$PERCENT_ENCODED_STRINGS[ord($ch)] = $ch;
65 }
66
67 //以下4个字符不编码
68 HttpUtil::$PERCENT_ENCODED_STRINGS[ord('-')] = '-';
69 HttpUtil::$PERCENT_ENCODED_STRINGS[ord('.')] = '.';
70 HttpUtil::$PERCENT_ENCODED_STRINGS[ord('_')] = '_';
71 HttpUtil::$PERCENT_ENCODED_STRINGS[ord('~')] = '~';
72 }
73
74 //在uri编码中不能对'/'编码
75 public static function urlEncodeExceptSlash($path)
76 {
77 return str_replace("%2F", "/", HttpUtil::urlEncode($path));
78 }
79
80 //使用编码数组编码
81 public static function urlEncode($value)
82 {
83 $result = '';
84 for ($i = 0; $i < strlen($value); ++$i) {
85 $result .= HttpUtil::$PERCENT_ENCODED_STRINGS[ord($value[$i])];
86 }
87 return $result;
88 }
89
90 //生成标准化QueryString
91 public static function getCanonicalQueryString(array $parameters)
92 {
93 //没有参数,直接返回空串
94 if (count($parameters) == 0) {
95 return '';
96 }
97
98 $parameterStrings = array();
99 foreach ($parameters as $k => $v) {
100 //跳过Authorization字段
101 if (strcasecmp('Authorization', $k) == 0) {
102 continue;
103 }
104 if (!isset($k)) {
105 throw new \InvalidArgumentException(
106 "parameter key should not be null"
107 );
108 }
109 if (isset($v)) {
110 //对于有值的,编码后放在=号两边
111 $parameterStrings[] = HttpUtil::urlEncode($k)
112 . '=' . HttpUtil::urlEncode((string) $v);
113 } else {
114 //对于没有值的,只将key编码后放在=号的左边,右边留空
115 $parameterStrings[] = HttpUtil::urlEncode($k) . '=';
116 }
117 }
118 //按照字典序排序
119 sort($parameterStrings);
120
121 //使用'&'符号连接它们
122 return implode('&', $parameterStrings);
123 }
124
125 //生成标准化uri
126 public static function getCanonicalURIPath($path)
127 {
128 //空路径设置为'/'
129 if (empty($path)) {
130 return '/';
131 } else {
132 //所有的uri必须以'/'开头
133 if ($path[0] == '/') {
134 return HttpUtil::urlEncodeExceptSlash($path);
135 } else {
136 return '/' . HttpUtil::urlEncodeExceptSlash($path);
137 }
138 }
139 }
140
141 //生成标准化http请求头串
142 public static function getCanonicalHeaders($headers)
143 {
144 //如果没有headers,则返回空串
145 if (count($headers) == 0) {
146 return '';
147 }
148
149 $headerStrings = array();
150 foreach ($headers as $k => $v) {
151 //跳过key为null的
152 if ($k === null) {
153 continue;
154 }
155 //如果value为null,则赋值为空串
156 if ($v === null) {
157 $v = '';
158 }
159 //trim后再encode,之后使用':'号连接起来
160 $headerStrings[] = HttpUtil::urlEncode(strtolower(trim($k))) . ':' . HttpUtil::urlEncode(trim($v));
161 }
162 //字典序排序
163 sort($headerStrings);
164
165 //用'\n'把它们连接起来
166 return implode("\n", $headerStrings);
167 }
168
169}
170HttpUtil::__init();
171
172
173class SampleSigner
174{
175
176 const BCE_AUTH_VERSION = "bce-auth-v1";
177 const BCE_PREFIX = 'x-bce-';
178
179 //不指定headersToSign情况下,默认签名http头,包括:
180 // 1.host
181 // 2.content-length
182 // 3.content-type
183 // 4.content-md5
184 public static $defaultHeadersToSign;
185
186 public static function __init()
187 {
188 SampleSigner::$defaultHeadersToSign = array(
189 "host",
190 "content-length",
191 "content-type",
192 "content-md5",
193 );
194 }
195
196 //签名函数
197 public function sign(
198 array $credentials,
199 $httpMethod,
200 $path,
201 $headers,
202 $params,
203 $options = array()
204 ) {
205 //设定签名有效时间
206 if (!isset($options[SignOption::EXPIRATION_IN_SECONDS])) {
207 //默认值1800秒
208 $expirationInSeconds = SignOption::DEFAULT_EXPIRATION_IN_SECONDS;
209 } else {
210 $expirationInSeconds = $options[SignOption::EXPIRATION_IN_SECONDS];
211 }
212
213 //解析ak sk
214 $accessKeyId = $credentials['ak'];
215 $secretAccessKey = $credentials['sk'];
216
217 //设定时间戳,注意:如果自行指定时间戳需要为UTC时间
218 if (!isset($options[SignOption::TIMESTAMP])) {
219 //默认值当前时间
220 $timestamp = new \DateTime();
221 } else {
222 $timestamp = $options[SignOption::TIMESTAMP];
223 }
224 $timestamp->setTimezone(new \DateTimeZone("UTC"));
225
226 //生成authString
227 $authString = SampleSigner::BCE_AUTH_VERSION . '/' . $accessKeyId . '/'
228 . $timestamp->format("Y-m-d\TH:i:s\Z") . '/' . $expirationInSeconds;
229
230 //使用sk和authString生成signKey
231 $signingKey = hash_hmac('sha256', $authString, $secretAccessKey);
232
233 //生成标准化URI
234 $canonicalURI = HttpUtil::getCanonicalURIPath($path);
235
236 //生成标准化QueryString
237 $canonicalQueryString = HttpUtil::getCanonicalQueryString($params);
238
239 //填充headersToSign,也就是指明哪些header参与签名
240 $headersToSignOption = null;
241 if (isset($options[SignOption::HEADERS_TO_SIGN])) {
242 $headersToSignOption = $options[SignOption::HEADERS_TO_SIGN];
243 }
244
245 $headersToSign = SampleSigner::getHeadersToSign($headers, $headersToSignOption);
246
247 //生成标准化header
248 $canonicalHeader = HttpUtil::getCanonicalHeaders($headersToSign);
249
250 $headersToSign = array_keys($headersToSign);
251 sort($headersToSign);
252 //整理headersToSign,以';'号连接
253 $signedHeaders = '';
254 if ($headersToSignOption !== null) {
255 $signedHeaders = strtolower(
256 trim(implode(";", $headersToSign))
257 );
258 }
259
260 //组成标准请求串
261 $canonicalRequest = "$httpMethod\n$canonicalURI\n"
262 . "$canonicalQueryString\n$canonicalHeader";
263
264 //使用signKey和标准请求串完成签名
265 $signature = hash_hmac('sha256', $canonicalRequest, $signingKey);
266
267 //组成最终签名串
268 $authorizationHeader = "$authString/$signedHeaders/$signature";
269
270 return $authorizationHeader;
271 }
272
273 /** 根据headsToSign过滤应该参与签名的header
274 *
275 * @param $headers array
276 * @param $headersToSign array
277 * @return array
278 */
279 public static function getHeadersToSign($headers, $headersToSign)
280 {
281
282 $ret = array();
283 if ($headersToSign !== null) {
284 $tmp = array();
285
286 //处理headers的key:去掉前后的空白并转化成小写
287 foreach ($headersToSign as $header) {
288 $tmp[] = strtolower(trim($header));
289 }
290 $headersToSign = $tmp;
291 }
292 foreach ($headers as $k => $v) {
293 if (trim((string) $v) !== '') {
294 if ($headersToSign !== null) {
295 //预处理headersToSign:去掉前后的空白并转化成小写
296 if (in_array(strtolower(trim($k)), $headersToSign)) {
297 $ret[$k] = $v;
298 }
299 } else {
300 //如果没有headersToSign,则根据默认规则来选取headers
301 if (SampleSigner::isDefaultHeaderToSign($k, $headersToSign)) {
302 $ret[$k] = $v;
303 }
304 }
305 }
306 }
307 return $ret;
308 }
309
310 /**
311 * 检查header是不是默认参加签名的:
312 * 1.是host、content-type、content-md5、content-length之一
313 * 2.以x-bce开头
314 *
315 * @param $header string
316 * @return bool
317 */
318 public static function isDefaultHeaderToSign($header)
319 {
320 $header = strtolower(trim($header));
321 if (in_array($header, SampleSigner::$defaultHeadersToSign)) {
322 return true;
323 }
324 $prefix = substr($header, 0, strlen(SampleSigner::BCE_PREFIX));
325 if ($prefix === SampleSigner::BCE_PREFIX) {
326 return true;
327 } else {
328 return false;
329 }
330 }
331}
332
333SampleSigner::__init();
334
335
336
337//签名示范代码
338$signer = new SampleSigner();
339$credentials = array("ak" => "0b0f67dfb88244b289b72b142befad0c","sk" => "bad522c2126a4618a8125f4b6cf6356f");
340$httpMethod = "PUT";
341$path = "/v1/test/myfolder/readme.txt";
342$headers = array("Host" => "bj.bcebos.com",
343 "Content-Length" => 8,
344 "Content-MD5" => "0a52730597fb4ffa01fc117d9e71e3a9",
345 "Content-Type" => "text/plain",
346 "x-bce-date" => "2015-04-27T08:23:49Z");
347$params = array("partNumber" => 9, "uploadId" => "VXBsb2FkIElpZS5tMnRzIHVwbG9hZA");
348date_default_timezone_set("PRC");
349$timestamp = new \DateTime();
350$timestamp->setTimestamp(1430123029);
351$options = array(SignOption::TIMESTAMP => $timestamp);
352// $options = array(SignOption::TIMESTAMP => $timestamp, SignOption::HEADERS_TO_SIGN => array("Content-Type", "Host", "x-bce-date"));
353$ret = $signer->sign($credentials, $httpMethod, $path, $headers, $params, $options);
354print $ret;
Java示例
用户可参考以下代码,进一步了解百度智能云API认证机制。
说明:Android语言的API认证示例代码和Java示例代码一致。
Plain Text
1/*
2 * Copyright (c) 2014 Baidu.com, Inc. All Rights Reserved
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
5 * the License. You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
10 * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
11 * specific language governing permissions and limitations under the License.
12 */
13
14package com.baidubce.auth;
15
16import com.baidubce.BceClientException;
17import com.baidubce.http.Headers;
18import com.baidubce.internal.InternalRequest;
19import com.baidubce.util.DateUtils;
20import com.baidubce.util.HttpUtils;
21import com.google.common.base.Joiner;
22import com.google.common.collect.Lists;
23import com.google.common.collect.Maps;
24import com.google.common.collect.Sets;
25
26import org.apache.commons.codec.binary.Hex;
27import org.slf4j.Logger;
28import org.slf4j.LoggerFactory;
29
30import javax.crypto.Mac;
31import javax.crypto.spec.SecretKeySpec;
32
33import java.nio.charset.Charset;
34import java.util.Collections;
35import java.util.Date;
36import java.util.List;
37import java.util.Map;
38import java.util.Set;
39import java.util.SortedMap;
40
41import static com.google.common.base.Preconditions.checkNotNull;
42
43/**
44 * The V1 implementation of Signer with the BCE signing protocol.
45 */
46public class BceV1Signer implements Signer {
47
48 private static final Logger logger = LoggerFactory.getLogger(BceV1Signer.class);
49
50 private static final String BCE_AUTH_VERSION = "bce-auth-v1";
51 private static final String DEFAULT_ENCODING = "UTF-8";
52 private static final Charset UTF8 = Charset.forName(DEFAULT_ENCODING);
53
54 // Default headers to sign with the BCE signing protocol.
55 private static final Set<String> defaultHeadersToSign = Sets.newHashSet();
56 private static final Joiner headerJoiner = Joiner.on('\n');
57 private static final Joiner signedHeaderStringJoiner = Joiner.on(';');
58
59 static {
60 BceV1Signer.defaultHeadersToSign.add(Headers.HOST.toLowerCase());
61 BceV1Signer.defaultHeadersToSign.add(Headers.CONTENT_LENGTH.toLowerCase());
62 BceV1Signer.defaultHeadersToSign.add(Headers.CONTENT_TYPE.toLowerCase());
63 BceV1Signer.defaultHeadersToSign.add(Headers.CONTENT_MD5.toLowerCase());
64 }
65
66 /**
67 * @see com.baidubce.auth.Signer#sign(InternalRequest, BceCredentials)
68 */
69 @Override
70 public void sign(InternalRequest request, BceCredentials credentials) {
71 this.sign(request, credentials, null);
72 }
73
74 /**
75 * Sign the given request with the given set of credentials. Modifies the passed-in request to apply the signature.
76 *
77 * @param request the request to sign.
78 * @param credentials the credentials to sign the request with.
79 * @param options the options for signing.
80 */
81 @Override
82 public void sign(InternalRequest request, BceCredentials credentials, SignOptions options) {
83 checkNotNull(request, "request should not be null.");
84
85 if (credentials == null) {
86 return;
87 }
88
89 if (options == null) {
90 if (request.getSignOptions() != null) {
91 options = request.getSignOptions();
92 } else {
93 options = SignOptions.DEFAULT;
94 }
95 }
96
97 String accessKeyId = credentials.getAccessKeyId();
98 String secretAccessKey = credentials.getSecretKey();
99
100 request.addHeader(Headers.HOST, HttpUtils.generateHostHeader(request.getUri()));
101
102 Date timestamp = options.getTimestamp();
103 if (timestamp == null) {
104 timestamp = new Date();
105 }
106
107 String authString =
108 BceV1Signer.BCE_AUTH_VERSION + "/" + accessKeyId + "/"
109 + DateUtils.formatAlternateIso8601Date(timestamp) + "/" + options.getExpirationInSeconds();
110
111 String signingKey = this.sha256Hex(secretAccessKey, authString);
112 // Formatting the URL with signing protocol.
113 String canonicalURI = this.getCanonicalURIPath(request.getUri().getPath());
114 // Formatting the query string with signing protocol.
115 String canonicalQueryString = HttpUtils.getCanonicalQueryString(request.getParameters(), true);
116 // Sorted the headers should be signed from the request.
117 SortedMap<String, String> headersToSign =
118 this.getHeadersToSign(request.getHeaders(), options.getHeadersToSign());
119 // Formatting the headers from the request based on signing protocol.
120 String canonicalHeader = this.getCanonicalHeaders(headersToSign);
121 String signedHeaders = "";
122 if (options.getHeadersToSign() != null) {
123 signedHeaders = BceV1Signer.signedHeaderStringJoiner.join(headersToSign.keySet());
124 signedHeaders = signedHeaders.trim().toLowerCase();
125 }
126
127 String canonicalRequest =
128 request.getHttpMethod() + "\n" + canonicalURI + "\n" + canonicalQueryString + "\n" + canonicalHeader;
129
130 // Signing the canonical request using key with sha-256 algorithm.
131 String signature = this.sha256Hex(signingKey, canonicalRequest);
132
133 String authorizationHeader = authString + "/" + signedHeaders + "/" + signature;
134
135 logger.debug("CanonicalRequest:{}\tAuthorization:{}", canonicalRequest.replace("\n", "[\\n]"),
136 authorizationHeader);
137
138 request.addHeader(Headers.AUTHORIZATION, authorizationHeader);
139 }
140
141 private String getCanonicalURIPath(String path) {
142 if (path == null) {
143 return "/";
144 } else if (path.startsWith("/")) {
145 return HttpUtils.normalizePath(path);
146 } else {
147 return "/" + HttpUtils.normalizePath(path);
148 }
149 }
150
151 private String getCanonicalHeaders(SortedMap<String, String> headers) {
152 if (headers.isEmpty()) {
153 return "";
154 }
155
156 List<String> headerStrings = Lists.newArrayList();
157 for (Map.Entry<String, String> entry : headers.entrySet()) {
158 String key = entry.getKey();
159 if (key == null) {
160 continue;
161 }
162 String value = entry.getValue();
163 if (value == null) {
164 value = "";
165 }
166 headerStrings.add(HttpUtils.normalize(key.trim().toLowerCase()) + ':' + HttpUtils.normalize(value.trim()));
167 }
168 Collections.sort(headerStrings);
169
170 return headerJoiner.join(headerStrings);
171 }
172
173 private SortedMap<String, String> getHeadersToSign(Map<String, String> headers, Set<String> headersToSign) {
174 SortedMap<String, String> ret = Maps.newTreeMap();
175 if (headersToSign != null) {
176 Set<String> tempSet = Sets.newHashSet();
177 for (String header : headersToSign) {
178 tempSet.add(header.trim().toLowerCase());
179 }
180 headersToSign = tempSet;
181 }
182 for (Map.Entry<String, String> entry : headers.entrySet()) {
183 String key = entry.getKey();
184 if (entry.getValue() != null && !entry.getValue().isEmpty()) {
185 if ((headersToSign == null && this.isDefaultHeaderToSign(key))
186 || (headersToSign != null && headersToSign.contains(key.toLowerCase())
187 && !Headers.AUTHORIZATION.equalsIgnoreCase(key))) {
188 ret.put(key, entry.getValue());
189 }
190 }
191 }
192 return ret;
193 }
194
195 private boolean isDefaultHeaderToSign(String header) {
196 header = header.trim().toLowerCase();
197 return header.startsWith(Headers.BCE_PREFIX) || defaultHeadersToSign.contains(header);
198 }
199
200 private String sha256Hex(String signingKey, String stringToSign) {
201 try {
202 Mac mac = Mac.getInstance("HmacSHA256");
203 mac.init(new SecretKeySpec(signingKey.getBytes(UTF8), "HmacSHA256"));
204 return new String(Hex.encodeHex(mac.doFinal(stringToSign.getBytes(UTF8))));
205 } catch (Exception e) {
206 throw new BceClientException("Fail to generate the signature", e);
207 }
208 }
209
210}
Javascript示例
用户可参考以下代码,进一步了解Javascript语言的百度智能云API认证机制。
代码下载路径:https://github.com/baidubce/bce-sdk-js/blob/master/test/sdk/auth.spec.js
Plain Text
1var Auth = require('@baiducloud/sdk').Auth;
2
3
4 var auth = new Auth('my_ak', 'my_sk');
5
6 var method = 'PUT';
7 var uri = '/v1/bucket/object1'; // 请勿传入已encode的字符串,sdk执行的时候会encode一次
8 var params = {
9 A: null,
10 b: '',
11 C: 'd'
12 };
13 var headers = {
14 'Host': 'bce.baidu.com',
15 'abc': '123',
16 'x-bce-meta-key1': 'ABC'
17 };
18 var headersToSign = ['Host', 'abc', 'x-bce-meta-key1'];
19
20 var signature = auth.generateAuthorization(method, uri, params, headers, 1402639056, 1800, headersToSign);
21 expect(signature).to.eql('bce-auth-v1/my_ak/2014-06-13T05:57:36Z/1800/host;x-bce-meta-key1/'
22 + '80c9672aca2ea9af4bb40b9a8ff458d72df94e97d550840727f3a929af271d25');
23
24 signature = auth.generateAuthorization(method, uri, params, headers, 1402639056, 1800, headersToSign);
25 expect(signature).to.eql('bce-auth-v1/my_ak/2014-06-13T05:57:36Z/1800/host;'
26 + 'x-bce-meta-key1/80c9672aca2ea9af4bb40b9a8ff458d72'
27 + 'df94e97d550840727f3a929af271d25');
28
29 method = 'DELETE';
30 uri = '/v1/test-bucket1361199862';
31 params = {};
32 headers = {
33 'Content-Type': 'application/json; charset=utf-8',
34 'Content-Length': 0,
35 'User-Agent': 'This is the user-agent'
36 };
37 signature = auth.generateAuthorization(method, uri, params, headers, 1402639056, 1800);
38 expect(signature).to.eql('bce-auth-v1/my_ak/2014-06-13T05:57:36Z/1800/'
39 + 'content-length;content-type/'
40 + 'c9386b15d585960ae5e6972f73ed92a9a682dc81025480ba5b41206d3e489822');
C#示例
用户可参考以下代码,进一步了解C#语言的百度智能云API认证机制。完整示例代码:
Plain Text
1using BaiduBce;
2using BaiduBce.Auth;
3using BaiduBce.Services.Bos;
4using BaiduBce.Services.Bos.Model;
5using BaiduBce.Services.Sts;
6using System;
7using System.Collections.Generic;
8using System.Collections.Specialized;
9using System.Diagnostics;
10using System.Globalization;
11using System.IO;
12using System.Net;
13using System.Security.Cryptography;
14using System.Text;
15using System.Web;
16
17namespace BOSTest
18{
19 class Program
20 {
21 static string UriEncode(string input, bool encodeSlash = false)
22 {
23 StringBuilder builder = new StringBuilder();
24 foreach (byte b in Encoding.UTF8.GetBytes(input))
25 {
26 if ((b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z') || (b >= '0' && b <= '9') || b == '_' || b == '-' || b == '~' || b == '.')
27 {
28 builder.Append((char)b);
29 }
30 else if (b == '/')
31 {
32 if (encodeSlash)
33 {
34 builder.Append("%2F");
35 }
36 else
37 {
38 builder.Append((char)b);
39 }
40 }
41 else
42 {
43 builder.Append('%').Append(b.ToString("X2"));
44 }
45 }
46 return builder.ToString();
47 }
48
49 static string Hex(byte[] data)
50 {
51 var sb = new StringBuilder();
52 foreach (var b in data)
53 {
54 sb.Append(b.ToString("x2"));
55 }
56 return sb.ToString();
57 }
58
59 static string CanonicalRequest(HttpWebRequest req)
60 {
61 Uri uri = req.RequestUri;
62 StringBuilder canonicalReq = new StringBuilder();
63 canonicalReq.Append(req.Method).Append("\n").Append(UriEncode(Uri.UnescapeDataString(uri.AbsolutePath))).Append("\n");
64
65 var parameters = HttpUtility.ParseQueryString(uri.Query);
66 List<string> parameterStrings = new List<string>();
67 foreach (KeyValuePair<string, string> entry in parameters)
68 {
69 parameterStrings.Add(UriEncode(entry.Key) + '=' + UriEncode(entry.Value));
70 }
71 parameterStrings.Sort();
72 canonicalReq.Append(string.Join("&", parameterStrings.ToArray())).Append("\n");
73
74 string host = uri.Host;
75 if (!(uri.Scheme == "https" && uri.Port == 443) && !(uri.Scheme == "http" && uri.Port == 80))
76 {
77 host += ":" + uri.Port;
78 }
79 canonicalReq.Append("host:" + UriEncode(host));
80 return canonicalReq.ToString();
81 }
82
83 static void Main(string[] args)
84 {
85 string bucket = "mybucket";
86 string key = "我的文件";
87 string ak = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
88 string sk = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";
89 DateTime now = DateTime.Now;
90 int expirationInSeconds = 1200;
91
92 HttpWebRequest req = WebRequest.Create("http://bj.bcebos.com/" + bucket + "/" + key) as HttpWebRequest;
93 Uri uri = req.RequestUri;
94 req.Method = "GET";
95
96 string signDate = now.ToUniversalTime().ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ssK");
97 Console.WriteLine(signDate);
98 string authString = "bce-auth-v1/" + ak + "/" + signDate + "/" + expirationInSeconds;
99 string signingKey = Hex(new HMACSHA256(Encoding.UTF8.GetBytes(sk)).ComputeHash(Encoding.UTF8.GetBytes(authString)));
100 Console.WriteLine(signingKey);
101
102 string canonicalRequestString = CanonicalRequest(req);
103 Console.WriteLine(canonicalRequestString);
104
105 string signature = Hex(new HMACSHA256(Encoding.UTF8.GetBytes(signingKey)).ComputeHash(Encoding.UTF8.GetBytes(canonicalRequestString)));
106 string authorization = authString + "/host/" + signature;
107 Console.WriteLine(authorization);
108
109 req.Headers.Add("x-bce-date", signDate);
110 req.Headers.Add(HttpRequestHeader.Authorization, authorization);
111
112 HttpWebResponse res;
113 string message = "";
114 try
115 {
116 res = req.GetResponse() as HttpWebResponse;
117 }
118 catch (WebException e)
119 {
120 res = e.Response as HttpWebResponse;
121 message = new StreamReader(res.GetResponseStream()).ReadToEnd();
122 }
123 Console.WriteLine((int)res.StatusCode);
124 Console.WriteLine(res.Headers);
125 Console.WriteLine(message);
126 Console.ReadLine();
127 }
128 }
129}
iOS示例
用户可参考以下代码,进一步了解iOS的百度智能云API认证机制。代码包含类代码和调用代码两部分,完整代码地址:代码。调用代码请参考:
Plain Text
1#import <XCTest/XCTest.h>
2#import "BDCloudSigner.h"
3
4@interface UT : XCTestCase
5@end
6
7@implementation UT
8
9id<BDCloudSigner> createSigner() {
10 BDCloudCredentials* credentials = [BDCloudCredentials new];
11 credentials.accessKey = @"<access key>";
12 credentials.secretKey = @"<secret key>";
13
14 id<BDCloudSigner> signer = [[BDCloudAKSKSigner alloc] initWithCredentials:credentials];
15 signer.expiredTimeInSeconds = 3600;
16
17 return signer;
18}
19
20NSMutableURLRequest* createRequest() {
21 // create url directly, or use NSURLComponents.
22 NSURL* url = [NSURL URLWithString:@"http://bj.bcebos.com/v1/bucket/object?append"];
23
24 // create request.
25 NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:url];
26 request.HTTPMethod = @"POST";
27 [request setValue:@"<length>" forHTTPHeaderField:@"Content-Length"];
28 [request setValue:@"<md5>" forHTTPHeaderField:@"Content-MD5"];
29 [request setValue:@"text/plain" forHTTPHeaderField:@"Content-Type"];
30
31 // custom metadata key should begin with lower case prefix 'x-bce-'.
32 [request setValue:@"2017-01-08T21:42:30Z" forHTTPHeaderField:@"x-bce-user-metadata-createtime"];
33
34 // Host will be set when call sign.
35 //[request setValue:@"bj.bcebos.com" forHTTPHeaderField:@"Host"];
36
37 return request;
38}
39
40void sign() {
41 id<BDCloudSigner> signer = createSigner();
42 NSMutableURLRequest* request = createRequest();
43 if (![signer sign:request]) {
44 return;
45 }
46
47 // url
48 NSURL* fileURL = [NSURL fileURLWithPath:@"<file path>"];
49
50 // send request
51 // sample purpose, don't care task will running correctly.
52 [[NSURLSession sharedSession] uploadTaskWithRequest:request
53 fromFile:fileURL];
54}
55
56- (void)testAKSKSigner {
57 sign();
58}
59
60@end
C++示例
用户可参考以下代码,进一步了解C++的百度智能云API认证机制。完整代码可下载百度智能云C++ sdk参考,下载地址:C++ SDK
Plain Text
1std::string DefaultSigner::generate_auth(HttpRequest *request, int expire_seconds) {
2 std::stringstream auth_str;
3 std::string sign_time = DateUtil::format_iso8601_date(time(NULL));
4 auth_str << "bce-auth-v1/" << _credential.ak() << "/" << sign_time << "/" << expire_seconds;
5 std::string sign_key = StringUtil::hmac_sha256_hex(_credential.sk(), auth_str.str());
6
7 std::ostringstream canonical_req;
8 // method
9 canonical_req << method_str(request->method()) << '\n';
10 // uri
11 canonical_req << StringUtil::url_encode(request->uri(), false) << '\n';
12 // query string
13 const StringMap ¶ms = request->parameters();
14 if (params.size() > 0) {
15 StringMap tmp;
16 for (StringMap::const_iterator it = params.begin(); it != params.end(); ++it) {
17 std::ostringstream p;
18 p << StringUtil::url_encode(StringUtil::trim(it->first)) << '='
19 << StringUtil::url_encode(StringUtil::trim(it->second));
20 tmp[p.str()] = "";
21 }
22 StringMap::iterator it = tmp.begin();
23 canonical_req << it->first;
24 for (++it; it != tmp.end(); ++it) {
25 canonical_req << '&' << it->first;
26 }
27 }
28 canonical_req << '\n';
29
30 bool use_sts = !_credential.sts_token().empty();
31 // header
32 canonical_req << "host:" << StringUtil::url_encode(request->host());
33 if (use_sts) {
34 canonical_req << "\nx-bce-security-token:" << StringUtil::url_encode(_credential.sts_token());
35 }
36 LOG(DEBUG) << "canonical request: [" << canonical_req.str() << ']';
37
38 std::string signature = StringUtil::hmac_sha256_hex(sign_key, canonical_req.str());
39 auth_str << "/host";
40 if (use_sts) {
41 auth_str << ";x-bce-security-token";
42 request->append_header("x-bce-security-token", _credential.sts_token());
43 }
44 auth_str << "/" << signature;
45 return auth_str.str();
46}
47
48void DefaultSigner::sign(HttpRequest *request) {
49 request->append_header("Authorization", generate_auth(request, _sign_expire_seconds));
50}