通用说明
更新时间:2025-10-17
API认证机制
所有API的安全认证一律采用Access Key与请求签名机制。Access Key由Access Key ID和Secret Access Key组成,均为字符串。对于每个HTTP请求,使用下面所描述的算法生成一个认证字符串。提交认证放在Authorization头域里。服务端根据生成算法验证认证字符串的正确性。认证字符串的格式为bce-auth-v{version}/{accessKeyId}/{timestamp}/{expirationPeriodInSeconds}/{signedHeaders}/{signature}。
- version是正整数,目前取值为1。
- timestamp 是生成签名时的 UTC 时间。
- expirationPeriodInSeconds表示签名有效期限。
- signedHeaders 是签名算法中涉及到的头域列表。头域名之间用分号(;)分隔,如 host;x-bce-date。列表按照字典序排列。(本 API 签名仅使用 host 和 x-bce-date 两个 header)
- signature是256位签名的十六进制表示,由64个小写字母组成。其生成算法见下。
当百度智能云接收到用户的请求后,系统将使用相同的 SK 和同样的认证机制生成认证字符串,并与用户请求中包含的认证字符串进行对比。如果认证字符串相同,系统认为用户拥有指定的操作权限,并执行相关操作;如果认证字符串不同,系统将忽略该操作并返回错误码。鉴权认证机制的详细内容请参见鉴权认证机制。
鉴权示例
python实现签名参考脚本
Python
1import datetime
2import hmac, hashlib
3import time
4import urllib.parse
5import requests
6
7
8def get_headers(url, method: str, ak, sk):
9 domain = urllib.parse.urlparse(url).netloc
10 uri = urllib.parse.urlparse(url).path
11 gcloud_params = {}
12 if url.find('?') > 0:
13 tmp = url.split('?')[1]
14 for item in tmp.split('&'):
15 if item.find('=') > 0:
16 gcloud_params[item.split('=')[0]] = item.split('=')[1]
17 else:
18 gcloud_params[item] = ""
19 gcloud_uri = url.split('?')[0]
20
21 headers = {
22 "x-bce-date": get_canonical_time(),
23 "Content-Type": "application/json",
24 "Host": domain,
25 "X-API-VERSION": "v2"
26 } #
27 bce_request = {
28 'uri': uri, # f"http://{domain}:8793/api/cce/service/v2/cluster/cce-lksgpvx5"
29 'params': gcloud_params,
30 'method': method.upper(),
31 'headers': headers
32 }
33 # print(bce_request)
34 auth = gen_authorization(bce_request, ak, sk)
35 headers['Authorization'] = get_utf8_value(auth)
36 return headers
37
38
39def get_canonical_time(timestamp=0):
40 """
41 Get cannonical time.
42
43 :type timestamp: int
44 :param timestamp: None
45 =======================
46 :return:
47 **string of canonical_time**
48 """
49 if timestamp == 0:
50 utctime = datetime.datetime.utcnow()
51 else:
52 utctime = datetime.datetime.utcfromtimestamp(timestamp)
53 return "%04d-%02d-%02dT%02d:%02d:%02dZ" % (
54 utctime.year, utctime.month, utctime.day,
55 utctime.hour, utctime.minute, utctime.second)
56
57
58def get_canonical_time(timestamp=0):
59 """
60 Get cannonical time.
61
62 :type timestamp: int
63 :param timestamp: None
64 =======================
65 :return:
66 **string of canonical_time**
67 """
68 if timestamp == 0:
69 utctime = datetime.datetime.utcnow()
70 else:
71 utctime = datetime.datetime.utcfromtimestamp(timestamp)
72 return "%04d-%02d-%02dT%02d:%02d:%02dZ" % (
73 utctime.year, utctime.month, utctime.day,
74 utctime.hour, utctime.minute, utctime.second)
75
76
77def gen_authorization(request, ak, sk, timestamp=None, expire_period=1800):
78 """
79 generate authorization string
80 if not specify timestamp, then use current time;
81 """
82 signedheaders = []
83 if "headers" in request:
84 signedheaders = list(key.lower() for key in request["headers"].keys() if key != '')
85 signedheaders.sort()
86 authorization = build_authorization(ak, signedheaders, expire_period, timestamp)
87 signingkey = _calc_signingkey(authorization, sk)
88 signature = _calc_signature(signingkey, request, signedheaders)
89 authorization["signature"] = signature
90 return serialize_authorization(authorization)
91
92
93def serialize_authorization(auth):
94 """
95 serialize Authorization object to authorization string
96 """
97 val = "/".join((auth['version'], auth['access'], auth['timestamp'], auth['period'],
98 ";".join(auth['signedheaders']), auth['signature']))
99 return get_utf8_value(val)
100
101
102def build_authorization(accesskey, signedheaders, period=1800, timestamp=None):
103 """
104 build Authorization object
105 """
106 auth = {}
107 auth['version'] = "bce-auth-v1"
108 auth['access'] = accesskey
109 if not timestamp:
110 auth['timestamp'] = get_canonical_time()
111 else:
112 auth['timestamp'] = timestamp
113 auth['period'] = str(period)
114 auth['signedheaders'] = signedheaders
115 return auth
116
117
118def _calc_signingkey(auth, sk):
119 """ Get a a signing key """
120 string_to_sign = "/".join((auth['version'], auth['access'],
121 auth['timestamp'], auth['period']))
122
123 signingkey = hmac.new(bytes(sk, 'utf-8'), bytes(string_to_sign, 'utf-8'),
124 hashlib.sha256).hexdigest()
125 return signingkey
126
127
128def get_utf8_value(value):
129 """
130 Get the UTF8-encoded version of a value.
131 """
132 if isinstance(value, bytes):
133 return value.decode('utf-8')
134 return value.encode('utf-8')
135
136
137def normalized_uri(uri):
138 """
139 Construct a normalized(except slash '/') uri
140 eg. /json-api/v1/example/ ==> /json-api/v1/example/
141 """
142 return urllib.parse.quote(get_utf8_value(uri), safe='-_.~/')
143
144
145def normalized(msg):
146 """
147 Construct a normalized uri
148 """
149 return urllib.parse.quote(get_utf8_value(msg), safe='-_.~')
150
151
152def canonical_qs(params):
153 """
154 Construct a sorted, correctly encoded query string
155 """
156 keys = list(params)
157 keys.sort()
158 pairs = []
159 for key in keys:
160 if key == "authorization":
161 continue
162 val = normalized(params[key])
163 pairs.append(urllib.parse.quote(key, safe='') + '=' + val)
164 qs = '&'.join(pairs)
165 return qs
166
167
168def canonical_header_str(headers, signedheaders=None):
169 """
170 calculate canonicalized header string
171 """
172 headers_norm_lower = dict()
173 for (k, v) in headers.items():
174 key_norm_lower = normalized(k.lower())
175 value_norm_lower = normalized(v.strip())
176 headers_norm_lower[key_norm_lower] = value_norm_lower
177 keys = list(headers_norm_lower)
178 keys.sort()
179 if "host" not in keys:
180 raise ValueError
181 header_list = []
182 default_signed = ("host", "content-length", "content-type", "content-md5")
183 if signedheaders:
184 for key in signedheaders:
185 key = normalized(key.lower())
186 if key not in keys:
187 raise ValueError
188 if headers_norm_lower[key]:
189 header_list.append(key + ":" + headers_norm_lower[key])
190 else:
191 for key in keys:
192 if key.startswith("x-bce-") or key in default_signed:
193 header_list.append(key + ":" + headers_norm_lower[key])
194 return '\n'.join(header_list)
195
196
197def _calc_signature(key, request, signedheaders):
198 """Generate BCE signature string."""
199 # Create canonical request
200 params = {}
201 headers = {}
202 # print request
203 if "params" in request:
204 params = request['params']
205 if "headers" in request:
206 headers = request['headers']
207 cr = "\n".join((request['method'].upper(),
208 normalized_uri(request['uri']),
209 canonical_qs(params),
210 canonical_header_str(headers, signedheaders)))
211 signature = hmac.new(bytes(key, 'utf-8'), bytes(cr, 'utf-8'), hashlib.sha256).hexdigest()
212 return signature
213
214
215def send_request(url, method, ak, sk, headers={}, data=None, json=None):
216 """
217 封装发送请求
218 """
219 headers.update(get_headers(url, method, ak, sk))
220 request_func = getattr(requests, method)
221 if data:
222 res = request_func(url, headers=headers, data=data)
223 elif json:
224 res = request_func(url, headers=headers, json=json)
225 else:
226 res = request_func(url, headers=headers)
227 return res.json()
228
229
230if __name__ == '__main__':
231 ak = "xxxx"
232 sk = "xxx"
233 # 方式1
234 jobcreate_url="http://aihc.bj.baidubce.com/?action=DescribeJobs&resourcePoolId=aihc-bhcmp"
235 print('-------方式1--------')
236 body = {
237 "queue":"xxx"
238 }
239 print(send_request(jobcreate_url, "post", ak, sk, json=body))
240 # 方式2
241 print('-------方式2--------')
242 get_headers(jobcreate_url, "post", ak, sk)
243 res = requests.post(jobcreate_url, headers=get_headers(jobcreate_url, "post", ak, sk), json=body).json()
244 print(res)
日期与时间规定
日期时间表示的地方一律采用UTC时间,遵循ISO 8601,并做以下约束:
- 表示日期一律采用YYYY-MM-DD方式,例如2014-06-01表示2014年6月1日
- 表示时间一律采用hh:mm:ss方式,并在最后加一个大写字母Z表示UTC时间。例如23:00:10Z表示UTC时间23点0分10秒。
- 凡涉及日期和时间合并表示时,在两者中间加大写字母T,例如2014-06-01T23:00:10Z表示UTC时间2014年6月1日23点0分10秒。
多语言请求示例
百度云已提供多语言基础SDK实现签名认证,您可以基于百度云SDK自行实现百舸接口请求,以下是几种常用语言的客户端实现示例
Python
依赖先安装百度云基础SDK pip install bce-python-sdk
Python
1import copy
2import json
3from baidubce import bce_base_client
4from baidubce.exception import BceHttpClientError, BceServerError
5from baidubce.auth import bce_v1_signer
6from baidubce.http import handler, bce_http_client, http_methods
7from baidubce.utils import required, Expando
8from baidubce.bce_client_configuration import BceClientConfiguration
9from baidubce.auth.bce_credentials import BceCredentials
10import logging
11logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s', force=True)
12logging.getLogger().setLevel(logging.INFO)
13logging.getLogger("baidubce").setLevel(logging.INFO)
14__logger = logging.getLogger(__name__)
15__logger.setLevel(logging.INFO)
16
17
18def parse_json(http_response, response):
19 """
20 解析HTTP响应中的JSON数据并更新response对象。
21
22 :param http_response: HTTP响应对象
23 :param response: 要更新的响应对象
24 :return: 解析是否成功
25 """
26 body = http_response.read()
27 if body:
28 try:
29 parsed_data = json.loads(body, object_hook=dict_to_python_object)
30 response.__dict__.update(parsed_data.__dict__)
31
32 # 移除metadata key(如果存在)
33 if 'metadata' in response.__dict__:
34 del response.__dict__['metadata']
35 except json.JSONDecodeError as e:
36 __logger.error('Failed to parse JSON response: %s', e)
37 return False
38 http_response.close()
39 return True
40
41
42def dict_to_python_object(d):
43 """
44 将字典转换为Expando对象,用于JSON解析时的对象钩子。
45
46 :param d: 要转换的字典
47 :return: Expando对象
48 """
49 attr = {}
50 for k, v in list(d.items()):
51 k = str(k)
52 attr[k] = v
53 return Expando(attr)
54
55def to_dict(obj):
56 if isinstance(obj, dict):
57 return {k: to_dict(v) for k, v in obj.items()}
58 elif hasattr(obj, '__dict__'):
59 return {k: to_dict(v) for k, v in obj.__dict__.items()}
60 elif isinstance(obj, list):
61 return [to_dict(i) for i in obj]
62 else:
63 return obj
64
65class AIHCV2Client(bce_base_client.BceBaseClient):
66 """
67 AIHC V2 base sdk client
68 """
69
70 version = b'v2'
71
72 def __init__(self, config=None):
73 bce_base_client.BceBaseClient.__init__(self, config)
74
75 def _compute_service_id(self):
76 return b'aihc'
77
78 def _merge_config(self, config=None):
79 if config is None:
80 return self.config
81 else:
82 new_config = copy.copy(self.config)
83 new_config.merge_non_none_values(config)
84 return new_config
85
86 def _send_request(self, http_method, path,
87 body=None, headers=None, params=None,
88 config=None, body_parser=None):
89 config = self._merge_config(config)
90 if body_parser is None:
91 body_parser = parse_json
92
93 if headers is None:
94 headers = {
95 b'version': AIHCV2Client.version,
96 b'Content-Type': b'application/json'
97 }
98 else:
99 headers[b'version'] = AIHCV2Client.version
100 headers[b'Content-Type'] = b'application/json'
101
102 return bce_http_client.send_request(
103 config, bce_v1_signer.sign, [handler.parse_error, body_parser],
104 http_method, path, body, headers, params)
105
106 def _send_job_request(self, http_method, path,
107 body=None, headers=None, params=None,
108 config=None, body_parser=None):
109 config = self._merge_config(config)
110 if body_parser is None:
111 body_parser = parse_json
112
113 if headers is None:
114 headers = {
115 b'X-API-Version': AIHCV2Client.version
116 }
117 else:
118 headers[b'X-API-Version'] = AIHCV2Client.version
119
120 return bce_http_client.send_request(
121 config, bce_v1_signer.sign, [handler.parse_error, body_parser],
122 http_method, path, body, headers, params)
123
124# ============任务相关接口============
125 @required(pageNumber=int, pageSize=int)
126 def DescribeJobs(
127 self,
128 resourcePoolId,
129 queueID=None,
130 queue=None,
131 status=None,
132 keywordType=None,
133 keywork=None,
134 orderBy=None,
135 order=None,
136 pageNumber=1,
137 pageSize=10
138 ):
139 """
140 查询训练任务列表。
141
142 参考文档:https://cloud.baidu.com/doc/AIHC/s/xmayvctia
143
144 :param resourcePoolId:
145 资源池唯一标识符(必填)
146 :type resourcePoolId: string
147
148 :param queueID:
149 托管资源池需传入该参数,为队列Id(可选,Query参数)
150 :type queueID: string
151
152 :param queue:
153 训练任务所属队列,通用资源池须填入队列名称,不填时返回所有。托管资源池须填入队列Id(可选,Body参数)
154 :type queue: string
155
156 :param status:
157 基于状态筛选任务(可选,Body参数)
158 :type status: string
159
160 :param keywordType:
161 筛选关键字类型(可选,Body参数)
162 :type keywordType: string
163
164 :param keywork:
165 关键字值,当前仅支持name/queueName(可选,Body参数)
166 :type keywork: string
167
168 :param orderBy:
169 排序字段,支持createdAt,finishedAt,默认为createdAt(可选,Body参数)
170 :type orderBy: string
171
172 :param order:
173 排序方式,可选 [asc, desc],asc为升序,desc为降序,默认desc(可选,Body参数)
174 :type order: string
175
176 :param pageNumber:
177 请求分页参数,表示第几页(可选,Body参数)
178 :type pageNumber: int
179
180 :param pageSize:
181 单页结果数,默认值为10(可选,Body参数)
182 :type pageSize: int
183
184 :return:
185 返回训练任务列表
186 :rtype: baidubce.bce_response.BceResponse
187 """
188 path = b'/'
189 params = {
190 'action': 'DescribeJobs',
191 'resourcePoolId': resourcePoolId,
192 }
193 if queueID:
194 params['queueID'] = queueID
195
196 body = {}
197 if queue is not None:
198 body['queue'] = queue
199 if status is not None:
200 body['status'] = status
201 if keywordType is not None:
202 body['keywordType'] = keywordType
203 if keywork is not None:
204 body['keywork'] = keywork
205 if orderBy is not None:
206 body['orderBy'] = orderBy
207 if order is not None:
208 body['order'] = order
209 if pageNumber is not None:
210 body['pageNumber'] = pageNumber
211 if pageSize is not None:
212 body['pageSize'] = pageSize
213
214 return self._send_job_request(
215 http_methods.POST,
216 path,
217 body=json.dumps(body),
218 params=params
219 )
220
221
222 @required(datasetId=str)
223 def ModifyDataset(self, datasetId, name=None, description=None, visibilityScope=None, visibilityUser=None, visibilityGroup=None):
224 """
225 修改数据集。
226
227 参考文档:https://cloud.baidu.com/doc/AIHC/s/Imc095v8z
228
229 :param datasetId: 数据集ID(必填,Query参数)
230 :type datasetId: str
231 :param name: 数据集名称(可选,Body参数)
232 :type name: str
233 :param description: 数据集描述(可选,Body参数)
234 :type description: str
235 :param visibilityScope: 可见范围(可选,Body参数)
236 可选值:ALL_PEOPLE(所有人可见)、ONLY_OWNER(仅所有者可读写)、USER_GROUP(指定范围可用)
237 :type visibilityScope: str
238 :param visibilityUser: 用户权限列表(可选,Body参数)
239 格式:[{"id": "xxx", "name": "xxx", "permission": "r"}, {"id": "yyy", "name": "xxx", "permission": "rw"}]
240 :type visibilityUser: list
241 :param visibilityGroup: 用户组权限列表(可选,Body参数)
242 格式:[{"id": "xxx", "name": "xxx", "permission": "r"}, {"id": "yyy", "name": "xxx", "permission": "rw"}]
243 :type visibilityGroup: list
244 :return: 修改结果
245 :rtype: baidubce.bce_response.BceResponse
246 """
247 path = b'/'
248 params = {
249 'action': 'ModifyDataset',
250 'datasetId': datasetId,
251 }
252 body = {}
253 if name is not None:
254 body['name'] = name
255 if description is not None:
256 body['description'] = description
257 if visibilityScope is not None:
258 body['visibilityScope'] = visibilityScope
259 if visibilityUser is not None:
260 body['visibilityUser'] = visibilityUser
261 if visibilityGroup is not None:
262 body['visibilityGroup'] = visibilityGroup
263 return self._send_request(
264 http_methods.POST,
265 path,
266 body=json.dumps(body),
267 params=params
268 )
269
270
271if __name__ == '__main__':
272 HOST = 'https://aihc.bj.baidubce.com'
273 AK = 'your-access-key-id'
274 SK = 'your-secret-access-key'
275
276 resourcePoolId = "cce-hcuw9ybk"
277 datasetId = "ds-CgyRs2EE"
278 name = "test-dataset-2xxx"
279
280 config = BceClientConfiguration(credentials=BceCredentials(AK, SK), endpoint=HOST)
281
282 # create a aihc client
283 aihc_client = AIHCV2Client(config)
284
285 # 查询任务列表
286 try:
287 __logger.info('-------------------DescribeJobs start--------------------------------')
288 response = aihc_client.DescribeJobs(resourcePoolId=resourcePoolId)
289 # 将response转换为python对象
290 print(json.dumps(to_dict(response), ensure_ascii=False))
291
292 # response对象的全部key
293 __logger.info('response.__dict__.keys(): %s', response.__dict__.keys())
294
295 except BceHttpClientError as e:
296 if isinstance(e.last_error, BceServerError):
297 __logger.error('send request failed. Response %s, code: %s, msg: %s'
298 % (e.last_error.status_code, e.last_error.code, e.last_error.message))
299 else:
300 __logger.error('send request failed. Unknown exception: %s' % e)
301
302 # 修改数据集
303 try:
304 __logger.info('-------------------ModifyDataset start--------------------------------')
305
306 # 示例1:仅修改数据集名称
307 response = aihc_client.ModifyDataset(datasetId=datasetId, name=name)
308 print("修改数据集名称结果:", response)
309 print(json.dumps(to_dict(response), ensure_ascii=False, indent=2))
310
311 except BceHttpClientError as e:
312 if isinstance(e.last_error, BceServerError):
313 __logger.error('send request failed. Response %s, code: %s, msg: %s'
314 % (e.last_error.status_code, e.last_error.code, e.last_error.message))
315 else:
316 __logger.error('send request failed. Unknown exception: %s' % e)
