access_key:BDxxxxx secret_key:xxxxx domain:域名 account:绑定的用户名
access_key,secret_key,domain 获取方式见接入须知
注意:使用OpenApi时,CDP资源权限与此绑定账户权限相关联,请勿越权访问
代码依赖 3 提供的 Sign 代码
获取 token 请求接口:
curl -X GET --location "https://e0-0-80cdp.datarangers-onpremise.volces.com/open_platform/openapi?account=admin&duration_seconds=3000&Action=QueryOpenPlatformOpenApi&Version=2021-12-16&ApiAction=getUserToken&ApiVersion=2023-10-19" \ -H "Accept: application/json" \ -H "Host: e0-0-80cdp.datarangers-onpremise.volces.com" \ -H "X-Date: 20240122T100402Z" \ -H "X-Content-Sha256: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" \ -H "Authorization: HMAC-SHA256 Credential=BDPPd6be69d8697587c8cd245f9bb32b9fcc/20240122/cn/openPlatform/request, SignedHeaders=host;x-content-sha256;x-date, Signature=c686da0f3235cc164839cd0db9b175f56d2d807aafcaa6d7f5342719a5ed41cf"
String xDate = Sign.getXDate(new Date()); // 20240122T100402Z
2. 将请求体 body 转为 byte[]
byte[] bytes = Sign.body2Bytes(body); // body 为 null
3. 获取 X-Content-Sha256
String content = Sign.hashSHA256(bytes); // e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
4. 构建 Authorization:
static String ak = "BDPPd6be69d8697587c8cd245f9bb32b9fcc"; static String sk = "632be27e66a8a07dd1c94c93fd8b8a6"; static String path = "https://xxxx/open_platform/openapi"; static String host = "e0-0-80cdp.datarangers-onpremise.volces.com"; Sign sign = new Sign("cn", "openPlatform", "http", host, ak, sk); String auth = sign.getAuthorization("GET", new HashMap<>() {{ put("account", "admin"); put("duration_seconds", "3000"); put("Action", "QueryOpenPlatformOpenApi"); put("Version", "2021-12-16"); put("ApiAction", "getUserToken"); put("ApiVersion", "2023-10-19"); }}, bytes, xDate); // HMAC-SHA256 Credential=BDPPd6be69d8697587c8cd245f9bb32b9fcc/20240122/cn/openPlatform/request, SignedHeaders=host;x-content-sha256;x-date, Signature=c686da0f3235cc164839cd0db9b175f56d2d807aafcaa6d7f5342719a5ed41cf
{ "code": 0, "message": "", "msg": "success", "data": { "current_time": "2024-01-22T18:04:21.325+08:00", "expired_time": "2024-01-22T18:54:21.325+08:00", "access_key": "BDPPa98d1e65418b880ba525a0267a73138a", "secret_key": "fb757c8db975fef79d440bb5f11c8454", "session_token": "STSeyJhdXRoX29iamVjdF9pZCI6MywiZXhwaXJlZF90aW1lIjoiMjAyNC0wMS0yMlQxODo1NDoyMS4zMjUrMDg6MDAiLCJhdXRob3JpemVkX3Byb2plY3RfaWRzIjpbMV0sImFjY291bnQiOiJhZG1pbiIsInNpZ25hdHVyZSI6IjMwNDYwMjIxMDBkYjM3YzQ4YTU1NDJhNWY1NzA0YjYyYzRlY2MxMzYzZGRhNTU5OTQyNzBiYWFmNGJmNzcyNzc0YmViYTQ2M2FlMDIyMTAwZDg1NjI4YjBmOTM2NDg1MTU2Y2I4MDMwMzRmNDA1YTI5MDEwNzgwN2UyYTRjYWU3OGJkOTE3MmI4MTkwZDlhZSJ9" } }
curl -X GET --location "https://e0-0-80cdp.datarangers-onpremise.volces.com/open_platform/openapi?current=1&pageSize=10&tenantId=1&Action=QueryOpenPlatformOpenApi&Version=2021-12-16&ApiAction=legacyGetSegmentList&ApiVersion=2023-02-10" \ -H "Accept: application/json" \ -H "X-Date: 20240122T100923Z" \ -H "X-Content-Sha256: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" \ -H "Authorization: HMAC-SHA256 Credential=BDPPa98d1e65418b880ba525a0267a73138a/20240122/cn/openPlatform/request, SignedHeaders=host;x-content-sha256;x-date, Signature=b86830497879b7aba0347e513a32a834c7b817ca9be5b9a369f7ed66dbbde6f7" \ -H "X-Cdp-Security-Token: STSeyJhdXRoX29iamVjdF9pZCI6MywiZXhwaXJlZF90aW1lIjoiMjAyNC0wMS0yMlQxODo1NDoyMS4zMjUrMDg6MDAiLCJhdXRob3JpemVkX3Byb2plY3RfaWRzIjpbMV0sImFjY291bnQiOiJhZG1pbiIsInNpZ25hdHVyZSI6IjMwNDYwMjIxMDBkYjM3YzQ4YTU1NDJhNWY1NzA0YjYyYzRlY2MxMzYzZGRhNTU5OTQyNzBiYWFmNGJmNzcyNzc0YmViYTQ2M2FlMDIyMTAwZDg1NjI4YjBmOTM2NDg1MTU2Y2I4MDMwMzRmNDA1YTI5MDEwNzgwN2UyYTRjYWU3OGJkOTE3MmI4MTkwZDlhZSJ9"
String xDate = Sign.getXDate(new Date()); // 20240122T100923Z
2. 将请求体 body 转为 byte[]
byte[] bytes = Sign.body2Bytes(body); // body 为 null
3. 获取 X-Content-Sha256
String content = Sign.hashSHA256(bytes); // e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
4. 构建 Authorization:
static String ak = "BDPPa98d1e65418b880ba525a0267a73138a"; static String sk = "fb757c8db975fef79d440bb5f11c8454"; static String path = "https://xxxx/open_platform/openapi"; static String host = "e0-0-80cdp.datarangers-onpremise.volces.com"; Sign sign = new Sign("cn", "openPlatform", "http", host, ak, sk); String auth = sign.getAuthorization("GET", new HashMap<>() {{ put("current", "1"); put("pageSize", "10"); put("tenantId", "1"); put("Action", "QueryOpenPlatformOpenApi"); put("Version", "2021-12-16"); put("ApiAction", "legacyGetSegmentList"); put("ApiVersion", "2023-02-10"); }}, bytes, xDate); // HMAC-SHA256 Credential=BDPPa98d1e65418b880ba525a0267a73138a/20240122/cn/openPlatform/request, SignedHeaders=host;x-content-sha256;x-date, Signature=b86830497879b7aba0347e513a32a834c7b817ca9be5b9a369f7ed66dbbde6f7
注意,获取临时 Token 的有效期在返回结果中 expired_time 字段,请尽量重复使用临时 Token 而不是反复获取 Token,以提高效率;
使用渠道账号中注册得到的 AK,SK,使用具体方法和 2.1 中签名获取 STS Token 的过程相同。
import com.squareup.okhttp.RequestBody; import okio.Buffer; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import java.net.URL; import java.nio.ByteBuffer; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.text.SimpleDateFormat; import java.util.*; import static java.util.HexFormat.*; /** * Copyright (year) Beijing Volcano Engine Technology Ltd. * * 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. */ public class Sign { private static final BitSet URLENCODER = new BitSet(256); private static final String CONST_ENCODE = "0123456789ABCDEF"; public static final Charset UTF_8 = StandardCharsets.UTF_8; private final String region; private final String service; private final String schema; private final String host; private final String path; private final String ak; private final String sk; static { int i; for (i = 97; i <= 122; ++i) { URLENCODER.set(i); } for (i = 65; i <= 90; ++i) { URLENCODER.set(i); } for (i = 48; i <= 57; ++i) { URLENCODER.set(i); } URLENCODER.set('-'); URLENCODER.set('_'); URLENCODER.set('.'); URLENCODER.set('~'); } public static byte[] body2Bytes(RequestBody body){ if (body == null) { return new byte[0]; } try { final Buffer buffer = new Buffer(); body.writeTo(buffer); return buffer.readByteArray(); } catch (final Exception e) { return new byte[0]; } } public Sign(String region, String service, String schema, String host, String ak, String sk) { this.region = region; this.service = service; this.host = host; this.schema = schema; this.path = "/open_platform/openapi"; this.ak = ak; this.sk = sk; } public static String getXDate(Date date) { SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd'T'HHmmss'Z'"); sdf.setTimeZone(TimeZone.getTimeZone("GMT")); return sdf.format(date); } public String getAuthorization(String method, Map queryList, byte[] body, String xDate) throws Exception { if (body == null) { body = new byte[0]; } String xContentSha256 = hashSHA256(body); String shortXDate = xDate.substring(0, 8); String signHeader = "host;x-content-sha256;x-date"; SortedMap realQueryList = new TreeMap<>(queryList); StringBuilder querySB = new StringBuilder(); for (String key : realQueryList.keySet()) { querySB.append(signStringEncoder(key)) .append("=") .append(signStringEncoder(realQueryList.get(key))) .append("&"); } querySB.deleteCharAt(querySB.length() - 1); String canonicalStringBuilder = method + "\n" + path + "\n" + querySB + "\n" + "host:" + host + "\n" + "x-content-sha256:" + xContentSha256 + "\n" + "x-date:" + xDate + "\n" + "\n" + signHeader + "\n" + xContentSha256; System.out.println(canonicalStringBuilder); String hashcanonicalString = hashSHA256(canonicalStringBuilder.getBytes()); String credentialScope = shortXDate + "/" + region + "/" + service + "/request"; String signString = "HMAC-SHA256" + "\n" + xDate + "\n" + credentialScope + "\n" + hashcanonicalString; byte[] signKey = genSigningSecretKeyV4(sk, shortXDate, region, service); String signature = of().formatHex(hmacSHA256(signKey, signString)); URL url = new URL(schema + "://" + host + path + "?" + querySB); return "HMAC-SHA256" + " Credential=" + ak + "/" + credentialScope + ", SignedHeaders=" + signHeader + ", Signature=" + signature; } private String signStringEncoder(String source) { if (source == null) { return null; } StringBuilder buf = new StringBuilder(source.length()); ByteBuffer bb = UTF_8.encode(source); while (bb.hasRemaining()) { int b = bb.get() & 255; if (URLENCODER.get(b)) { buf.append((char) b); } else if (b == 32) { buf.append("%20"); } else { buf.append("%"); char hex1 = CONST_ENCODE.charAt(b >> 4); char hex2 = CONST_ENCODE.charAt(b & 15); buf.append(hex1); buf.append(hex2); } } return buf.toString(); } public static String hashSHA256(byte[] content) throws Exception { try { MessageDigest md = MessageDigest.getInstance("SHA-256"); return of().formatHex(md.digest(content)); } catch (Exception e) { throw new Exception("Unable to compute hash while signing request: " + e.getMessage(), e); } } public static byte[] hmacSHA256(byte[] key, String content) throws Exception { try { Mac mac = Mac.getInstance("HmacSHA256"); mac.init(new SecretKeySpec(key, "HmacSHA256")); return mac.doFinal(content.getBytes()); } catch (Exception e) { throw new Exception("Unable to calculate a request signature: " + e.getMessage(), e); } } private byte[] genSigningSecretKeyV4(String secretKey, String date, String region, String service) throws Exception { byte[] kDate = hmacSHA256((secretKey).getBytes(), date); byte[] kRegion = hmacSHA256(kDate, region); byte[] kService = hmacSHA256(kRegion, service); return hmacSHA256(kService, "request"); } }