百度智能云鉴权Demo示例
更新时间:2024-09-20
百度智能云鉴权Demo示例
说明: 在面临复杂的工程环境中经常出现依赖版本冲突,故提供无必要外部依赖的代码示例。 注:百度智能云兼容AWS的鉴权方式,本示例也进行提供,请按需接入获取。
本文档整理了在JAVA中常见的http工具,具体选取以实际工程选其一即可,另文档中只有执行示例。详细请参考鉴权认证机制
以下为当前示例运行环境,实际使用可自行调整:
环境 | 版本 |
---|---|
JDK | 1.8 |
Maven | 3.9.x |
Okhttp3 | 4.12.0 |
Apache Httpclient | 4.5.13 |
说明: 注意:核心代码中的ak/sk需替换为百度智能云账号下的ak/sk管理您的AKSK,以及具体的业务接口。点击下载完整示例代码
OkHttp3示例(推荐)
Maven依赖
Java
1 <dependency>
2 <groupId>com.squareup.okhttp3</groupId>
3 <artifactId>okhttp</artifactId>
4 <version>4.12.0</version>
5 </dependency>
6 <dependency>
7 <groupId>junit</groupId>
8 <artifactId>junit</artifactId>
9 <version>4.13.2</version>
10 </dependency>
baiduV1版本(推荐)
核心代码
Java
1import javax.crypto.Mac;
2import javax.crypto.spec.SecretKeySpec;
3import java.io.UnsupportedEncodingException;
4import java.nio.charset.Charset;
5import java.time.LocalDateTime;
6import java.time.ZoneId;
7import java.util.ArrayList;
8import java.util.BitSet;
9import java.util.Collections;
10import java.util.HashSet;
11import java.util.List;
12import java.util.Map;
13import java.util.Set;
14import java.util.SortedMap;
15import java.util.TreeMap;
16
17
18public class BaiduV1Singer {
19
20 !!! todo 修改成自己的ak和sk
21
22 private static final String ACCESS_KEY = "your_ak";
23 private static final String SECRET_KEY = "your_sk";
24
25
26
27
28 public static final String HOST = "Host";
29 public static final String AUTHORIZATION = "Authorization";
30 public static final String CONTENT_LENGTH = "Content-Length";
31 public static final String CONTENT_MD5 = "Content-MD5";
32 public static final String CONTENT_TYPE = "Content-Type";
33 public static final String BCE_PREFIX = "x-bce-";
34
35 private static final String BCE_AUTH_VERSION = "bce-auth-v1";
36 private static final String DEFAULT_ENCODING = "UTF-8";
37 private static final Charset UTF8 = Charset.forName(DEFAULT_ENCODING);
38 private static final BitSet URI_UNRESERVED_CHARACTERS = new BitSet();
39 private static final String[] PERCENT_ENCODED_STRINGS = new String[256];
40 private static final Set<String> defaultHeadersToSign = new HashSet<>();
41 private static final java.time.format.DateTimeFormatter formatter =
42 java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss'Z'");
43
44
45 static {
46 defaultHeadersToSign.add(HOST.toLowerCase());
47 defaultHeadersToSign.add(CONTENT_LENGTH.toLowerCase());
48 defaultHeadersToSign.add(CONTENT_TYPE.toLowerCase());
49 defaultHeadersToSign.add(CONTENT_MD5.toLowerCase());
50 }
51
52 static {
53 for (int i = 'a'; i <= 'z'; i++) {
54 URI_UNRESERVED_CHARACTERS.set(i);
55 }
56 for (int i = 'A'; i <= 'Z'; i++) {
57 URI_UNRESERVED_CHARACTERS.set(i);
58 }
59 for (int i = '0'; i <= '9'; i++) {
60 URI_UNRESERVED_CHARACTERS.set(i);
61 }
62 URI_UNRESERVED_CHARACTERS.set('-');
63 URI_UNRESERVED_CHARACTERS.set('.');
64 URI_UNRESERVED_CHARACTERS.set('_');
65 URI_UNRESERVED_CHARACTERS.set('~');
66
67 for (int i = 0; i < PERCENT_ENCODED_STRINGS.length; ++i) {
68 PERCENT_ENCODED_STRINGS[i] = String.format("%%%02X", i);
69 }
70 }
71
72 public static String sign(String uri, String method, Map<String, String> parameters, Map<String, String> headers) {
73
74 String authString =
75 BCE_AUTH_VERSION + "/" + ACCESS_KEY + "/"
76 + LocalDateTime.now(ZoneId.of("UTC")).format(formatter) + "/" + 1800;
77 String signingKey = sha256Hex(SECRET_KEY, authString);
78
79 String canonicalURI = getCanonicalURIPath(uri);
80
81 String canonicalQueryString = getCanonicalQueryString(parameters, true);
82
83 SortedMap<String, String> headersToSign = getHeadersToSign(headers, null);
84
85 String canonicalHeader = getCanonicalHeaders(headersToSign);
86
87 String signedHeaders;
88 signedHeaders = String.join(";", headersToSign.keySet());
89 signedHeaders = signedHeaders.trim().toLowerCase();
90
91 String canonicalRequest =
92 method + "\n" + canonicalURI + "\n" + canonicalQueryString + "\n" + canonicalHeader;
93
94 System.out.println("authString :" + authString);
95 System.out.println("signingKey :" + signingKey);
96 System.out.println("canonicalRequest: " + canonicalRequest);
97
98 // Signing the canonical request using key with sha-256 algorithm.
99 String signature = sha256Hex(signingKey, canonicalRequest);
100
101 return authString + "/" + signedHeaders + "/" + signature;
102 }
103
104
105 private static String sha256Hex(String signingKey, String stringToSign) {
106 try {
107 Mac mac = Mac.getInstance("HmacSHA256");
108 mac.init(new SecretKeySpec(signingKey.getBytes(UTF8), "HmacSHA256"));
109 return bytesToHex(mac.doFinal(stringToSign.getBytes(UTF8)));
110 } catch (Exception e) {
111 throw new RuntimeException("Fail to generate the signature", e);
112 }
113 }
114 private static String bytesToHex(byte[] bytes) {
115 StringBuilder result = new StringBuilder();
116 for (byte b : bytes) {
117 result.append(String.format("%02x", b));
118 }
119 return result.toString();
120 }
121 private static String getCanonicalURIPath(String path) {
122 if (path == null) {
123 return "/";
124 } else if (path.startsWith("/")) {
125 return normalizePath(path);
126 } else {
127 return "/" + normalizePath(path);
128 }
129 }
130
131 private static String normalize(String value) {
132 try {
133 StringBuilder builder = new StringBuilder();
134 for (byte b : value.getBytes(DEFAULT_ENCODING)) {
135 if (URI_UNRESERVED_CHARACTERS.get(b & 0xFF)) {
136 builder.append((char) b);
137 } else {
138 builder.append(PERCENT_ENCODED_STRINGS[b & 0xFF]);
139 }
140 }
141 return builder.toString();
142 } catch (UnsupportedEncodingException e) {
143 throw new RuntimeException(e);
144 }
145 }
146
147 private static String normalizePath(String path) {
148 return normalize(path).replace("%2F", "/");
149 }
150
151
152 private static String getCanonicalQueryString(Map<String, String> parameters, boolean forSignature) {
153 if (parameters.isEmpty()) {
154 return "";
155 }
156
157 List<String> parameterStrings = new ArrayList<>();
158 for (Map.Entry<String, String> entry : parameters.entrySet()) {
159 if (forSignature && AUTHORIZATION.equalsIgnoreCase(entry.getKey())) {
160 continue;
161 }
162 String key = entry.getKey();
163 checkNotNull(key, "parameter key should not be null");
164 String value = entry.getValue();
165 if (value == null) {
166 if (forSignature) {
167 parameterStrings.add(normalize(key) + '=');
168 } else {
169 parameterStrings.add(normalize(key));
170 }
171 } else {
172 parameterStrings.add(normalize(key) + '=' + normalize(value));
173 }
174 }
175 Collections.sort(parameterStrings);
176
177 return String.join("&",parameterStrings);
178 }
179
180 private static <T> T checkNotNull(T reference, Object errorMessage) {
181 if (reference == null) {
182 throw new NullPointerException(String.valueOf(errorMessage));
183 } else {
184 return reference;
185 }
186 }
187
188 private static SortedMap<String, String> getHeadersToSign(Map<String, String> headers, Set<String> headersToSign) {
189 SortedMap<String, String> ret = new TreeMap<>();
190 if (headersToSign != null) {
191 Set<String> tempSet = new HashSet<>();
192 for (String header : headersToSign) {
193 tempSet.add(header.trim().toLowerCase());
194 }
195 headersToSign = tempSet;
196 }
197 for (Map.Entry<String, String> entry : headers.entrySet()) {
198 String key = entry.getKey();
199 if (entry.getValue() != null && !entry.getValue().isEmpty()) {
200 if ((headersToSign == null && isDefaultHeaderToSign(key))
201 || (headersToSign != null && headersToSign.contains(key.toLowerCase())
202 && !AUTHORIZATION.equalsIgnoreCase(key))) {
203 ret.put(key, entry.getValue());
204 }
205 }
206 }
207 return ret;
208 }
209
210 private static boolean isDefaultHeaderToSign(String header) {
211 header = header.trim().toLowerCase();
212 return header.startsWith(BCE_PREFIX) || defaultHeadersToSign.contains(header);
213 }
214
215 private static String getCanonicalHeaders(SortedMap<String, String> headers) {
216 if (headers.isEmpty()) {
217 return "";
218 }
219
220 List<String> headerStrings = new ArrayList<>();
221 for (Map.Entry<String, String> entry : headers.entrySet()) {
222 String key = entry.getKey();
223 if (key == null) {
224 continue;
225 }
226 String value = entry.getValue();
227 if (value == null) {
228 value = "";
229 }
230 headerStrings.add(normalize(key.trim().toLowerCase()) + ':' + normalize(value.trim()));
231 }
232 Collections.sort(headerStrings);
233
234 return String.join("\n", headerStrings);
235 }
236
237}
测试代码
Java
1import okhttp3.MediaType;
2import okhttp3.OkHttpClient;
3import okhttp3.Request;
4import okhttp3.RequestBody;
5import okhttp3.Response;
6import org.intellij.lang.annotations.Language;
7import org.junit.Test;
8
9import java.nio.charset.StandardCharsets;
10import java.util.HashMap;
11import java.util.Map;
12
13import static com.baidu.duhome.BaiduV1Singer.sign;
14import static com.sun.deploy.net.HttpRequest.CONTENT_TYPE;
15
16public class BaiduV1SingerTest {
17
18
19 @Test
20 public void okhttpPostTest() throws Exception {
21
22 String host = "smarthome-bdvs.baidubce.com";
23
24 !!! 具体的业务接口
25 String uri = "/v1/dynamicDict/list";
26
27 String url = "https://" + host + uri;
28
29 !!! todo: 请求体
30 @Language("JSON") String requestBody = "{\n" +
31 " \"fc\": \"device_fc\",\n" +
32 " \"pk\": \"device_pk\",\n" +
33 " \"ak\": \"mock_ak02\",\n" +
34 " \"normValue\": \"A01\",\n" +
35 " \"synonymValue\": \"xxxx" +
36 "\"\n" +
37 "}";
38
39 Map<String, String> hashMap = new HashMap<>();
40 hashMap.put("Content-Type", "application/json");
41 hashMap.put("HOST", host);
42
43 OkHttpClient client = new OkHttpClient();
44 MediaType mediaType = MediaType.parse(CONTENT_TYPE);
45 Request request = new Request.Builder()
46 .header("Content-Type", "application/json")
47 .header("Host", host)
48 .header("Authorization", sign(uri, "POST", new HashMap<>(), hashMap))
49 // 构建requestBody 不能使用字符的方法,可以使用字节数组或者流的方式,字符串的方法中会强制在请求头中增加 charaset=urf8; 导致鉴权失败
50 .post(RequestBody.create(requestBody.getBytes(StandardCharsets.UTF_8), mediaType))
51 .url(url)
52 .build();
53
54
55 try (Response execute = client.newCall(request).execute()) {
56 String string = null;
57 if (execute.body() != null) {
58 string = execute.body().string();
59 }
60 System.out.println(string);
61 }
62
63 }
64
65 @Test
66 public void okhttpGetTest() throws Exception {
67 String host = "smarthome-bdvs.baidubce.com";
68 String uri = "/v1/foo";
69 String url = "http://" + host + uri;
70
71 Map<String, String> hashMap = new HashMap<>();
72 hashMap.put("HOST", host);
73
74 OkHttpClient client = new OkHttpClient();
75 Request request = new Request.Builder()
76 .header("Host", host)
77 .header("Authorization", sign(uri, "GET", new HashMap<>(), hashMap))
78 .get()
79 .url(url)
80 .build();
81
82 try (Response execute = client.newCall(request).execute()) {
83 String string = null;
84 if (execute.body() != null) {
85 string = execute.body().string();
86 }
87 System.out.println(string);
88 }
89 }
90
91}
AWS版本
核心代码
Java
1import okhttp3.Request;
2
3import javax.crypto.Mac;
4import javax.crypto.spec.SecretKeySpec;
5import java.net.URI;
6import java.net.URISyntaxException;
7import java.nio.charset.StandardCharsets;
8import java.security.MessageDigest;
9import java.security.NoSuchAlgorithmException;
10import java.time.LocalDateTime;
11import java.time.ZoneId;
12import java.time.format.DateTimeFormatter;
13
14public class AwsOkhttp3Signer {
15
16 private static final String ACCESS_KEY = "your_ak";
17 private static final String SECRET_KEY = "your_sk";
18
19
20 private static final String CONTENT_TYPE = "application/json";
21
22 /**
23 * 固定值
24 */
25 private static final String REGION = "us-east-1";
26
27 /**
28 * 固定值
29 */
30 private static final String SERVICE = "execute-api";
31
32
33 /**
34 * 生成带有签名的请求
35 *
36 * @param request 需要签名的请求
37 * @param requestBody 请求体
38 * @return 带有签名的请求
39 * @throws Exception 签名过程中可能发生的异常
40 */
41 public static Request getSignedRequest(Request request, String requestBody) throws Exception {
42 String format = getAuthDate();
43 String authorizationHeader = getAuthorizationHeader(request.url().toString(), request.method(), requestBody, format);
44 return request.newBuilder()
45 .addHeader("Content-Type", CONTENT_TYPE)
46 .addHeader("Authorization", authorizationHeader)
47 .addHeader("X-Amz-Content-Sha256", calculateSHA256(requestBody))
48 .addHeader("X-Amz-Date", format)
49 .build();
50 }
51
52
53 private static String getAuthDate() {
54 return LocalDateTime.now(ZoneId.of("UTC")).format(DateTimeFormatter.ofPattern("yyyyMMdd'T'HHmmss'Z'"));
55 }
56
57 public static String getAuthorizationHeader(String url, String method, String requestBody, String amzDate) throws Exception {
58
59 URI uri;
60 try {
61 uri = new URI(url);
62 } catch (URISyntaxException e) {
63 throw new Exception("Invalid URL: " + url);
64 }
65 String host = uri.getAuthority();
66 String canonicalUri = uri.getPath();
67 String canonicalQuerystring = uri.getQuery();
68 String contentHash = calculateSHA256(requestBody);
69
70 String canonicalHeaders = "content-type:" + CONTENT_TYPE + "\n" +
71 "host:" + host + "\n" +
72 "x-amz-content-sha256:" + contentHash + "\n" +
73 "x-amz-date:" + amzDate + "\n";
74
75 String signedHeaders = "content-type;host;x-amz-content-sha256;x-amz-date";
76 String canonicalRequest = method + "\n" + canonicalUri + "\n" + (canonicalQuerystring == null ? "" : canonicalQuerystring) + "\n" +
77 canonicalHeaders + "\n" + signedHeaders + "\n" + contentHash;
78 String credentialScope = amzDate.substring(0, 8) + "/" + REGION + "/" + SERVICE + "/aws4_request";
79 String stringToSign = "AWS4-HMAC-SHA256\n" + amzDate + "\n" + credentialScope + "\n" +
80 calculateSHA256(canonicalRequest);
81
82 byte[] signingKey = getSigningKey(amzDate.substring(0, 8));
83 String signature = calculateHMAC(stringToSign, signingKey);
84
85 return "AWS4-HMAC-SHA256 Credential=" + ACCESS_KEY + "/" + credentialScope + ", " +
86 "SignedHeaders=" + signedHeaders + ", " + "Signature=" + signature;
87 }
88
89 private static String calculateSHA256(String text) throws NoSuchAlgorithmException {
90 MessageDigest digest = MessageDigest.getInstance("SHA-256");
91 byte[] hash = digest.digest(text.getBytes(StandardCharsets.UTF_8));
92 return bytesToHex(hash);
93 }
94
95 private static String bytesToHex(byte[] bytes) {
96 StringBuilder result = new StringBuilder();
97 for (byte b : bytes) {
98 result.append(String.format("%02x", b));
99 }
100 return result.toString();
101 }
102
103 private static byte[] getSigningKey(String dateStamp) throws Exception {
104 byte[] kSecret = ("AWS4" + SECRET_KEY).getBytes(StandardCharsets.UTF_8);
105 byte[] kDate = hmacSHA256(dateStamp, kSecret);
106 byte[] kRegion = hmacSHA256(REGION, kDate);
107 byte[] kService = hmacSHA256(SERVICE, kRegion);
108 return hmacSHA256("aws4_request", kService);
109 }
110
111 private static byte[] hmacSHA256(String data, byte[] key) throws Exception {
112 Mac mac = Mac.getInstance("HmacSHA256");
113 SecretKeySpec keySpec = new SecretKeySpec(key, "HmacSHA256");
114 mac.init(keySpec);
115 return mac.doFinal(data.getBytes(StandardCharsets.UTF_8));
116 }
117
118 private static String calculateHMAC(String data, byte[] key) throws Exception {
119 byte[] hmacData = hmacSHA256(data, key);
120 return bytesToHex(hmacData);
121 }
122
123
124}
测试代码
Java
1import okhttp3.MediaType;
2import okhttp3.OkHttpClient;
3import okhttp3.Request;
4import okhttp3.RequestBody;
5import okhttp3.Response;
6
7import java.nio.charset.StandardCharsets;
8
9import static com.baidu.duhome.AwsOkhttp3Signer.getSignedRequest;
10import static com.sun.deploy.net.HttpRequest.CONTENT_TYPE;
11
12public class Okhhtp3SignerTest {
13
14 @org.junit.Test
15 public void postApiTest() throws Exception {
16 !!! 具体业务地址
17 String url = "http://smarthome-bdvs.baidubce.com/v1/dynamicDict/list";
18
19 !!! todo: 请求体
20 String requestBody = "{\n" +
21 " \"fc\": \"device_fc\",\n" +
22 " \"pk\": \"device_pk\",\n" +
23 " \"ak\": \"mock_ak01\",\n" +
24 " \"normValue\": \"A01\",\n" +
25 " \"synonymValue\": \"xxxx\"\n" +
26 "}";
27
28 OkHttpClient client = new OkHttpClient();
29 MediaType mediaType = MediaType.parse(CONTENT_TYPE);
30 Request request = new Request.Builder()
31 // 构建requestBody 不能使用字符的方法,可以使用字节数组或者流的方式,字符串的方法中会强制在请求头中增加 charaset=urf8; 导致鉴权失败
32 .post(RequestBody.create(requestBody.getBytes(StandardCharsets.UTF_8), mediaType))
33 .url(url)
34 .build();
35
36 // (进行签名) 注意会生成一个新的Request对象。
37 request = getSignedRequest(request, requestBody);
38
39 try (Response execute = client.newCall(request).execute()) {
40 String string = null;
41 if (execute.body() != null) {
42 string = execute.body().string();
43 }
44 System.out.println(string);
45 }
46
47 }
48}
ApacheHttpClient示例
Maven依赖
Java
1 <dependency>
2 <groupId>org.apache.httpcomponents</groupId>
3 <artifactId>httpclient</artifactId>
4 <version>4.5.13</version>
5 </dependency>
6 <dependency>
7 <groupId>junit</groupId>
8 <artifactId>junit</artifactId>
9 <version>4.13.2</version>
10 </dependency>
baiduV1版本(推荐)
核心代码
Java
1import javax.crypto.Mac;
2import javax.crypto.spec.SecretKeySpec;
3import java.io.UnsupportedEncodingException;
4import java.nio.charset.Charset;
5import java.time.LocalDateTime;
6import java.time.ZoneId;
7import java.util.ArrayList;
8import java.util.BitSet;
9import java.util.Collections;
10import java.util.HashSet;
11import java.util.List;
12import java.util.Map;
13import java.util.Set;
14import java.util.SortedMap;
15import java.util.TreeMap;
16
17
18public class BaiduV1Singer {
19
20 !!! todo 修改成自己的ak和sk
21 private static final String ACCESS_KEY = "your_ak";
22 private static final String SECRET_KEY = "your_sk";
23
24
25 public static final String HOST = "Host";
26 public static final String AUTHORIZATION = "Authorization";
27 public static final String CONTENT_LENGTH = "Content-Length";
28 public static final String CONTENT_MD5 = "Content-MD5";
29 public static final String CONTENT_TYPE = "Content-Type";
30 public static final String BCE_PREFIX = "x-bce-";
31
32 private static final String BCE_AUTH_VERSION = "bce-auth-v1";
33 private static final String DEFAULT_ENCODING = "UTF-8";
34 private static final Charset UTF8 = Charset.forName(DEFAULT_ENCODING);
35 private static final BitSet URI_UNRESERVED_CHARACTERS = new BitSet();
36 private static final String[] PERCENT_ENCODED_STRINGS = new String[256];
37 private static final Set<String> defaultHeadersToSign = new HashSet<>();
38 private static final java.time.format.DateTimeFormatter formatter =
39 java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss'Z'");
40
41
42 static {
43 defaultHeadersToSign.add(HOST.toLowerCase());
44 defaultHeadersToSign.add(CONTENT_LENGTH.toLowerCase());
45 defaultHeadersToSign.add(CONTENT_TYPE.toLowerCase());
46 defaultHeadersToSign.add(CONTENT_MD5.toLowerCase());
47 }
48
49 static {
50 for (int i = 'a'; i <= 'z'; i++) {
51 URI_UNRESERVED_CHARACTERS.set(i);
52 }
53 for (int i = 'A'; i <= 'Z'; i++) {
54 URI_UNRESERVED_CHARACTERS.set(i);
55 }
56 for (int i = '0'; i <= '9'; i++) {
57 URI_UNRESERVED_CHARACTERS.set(i);
58 }
59 URI_UNRESERVED_CHARACTERS.set('-');
60 URI_UNRESERVED_CHARACTERS.set('.');
61 URI_UNRESERVED_CHARACTERS.set('_');
62 URI_UNRESERVED_CHARACTERS.set('~');
63
64 for (int i = 0; i < PERCENT_ENCODED_STRINGS.length; ++i) {
65 PERCENT_ENCODED_STRINGS[i] = String.format("%%%02X", i);
66 }
67 }
68
69 public static String sign(String uri, String method, Map<String, String> parameters, Map<String, String> headers) {
70
71 String authString =
72 BCE_AUTH_VERSION + "/" + ACCESS_KEY + "/"
73 + LocalDateTime.now(ZoneId.of("UTC")).format(formatter) + "/" + 1800;
74 String signingKey = sha256Hex(SECRET_KEY, authString);
75
76 String canonicalURI = getCanonicalURIPath(uri);
77
78 String canonicalQueryString = getCanonicalQueryString(parameters, true);
79
80 SortedMap<String, String> headersToSign = getHeadersToSign(headers, null);
81
82 String canonicalHeader = getCanonicalHeaders(headersToSign);
83
84 String signedHeaders;
85 signedHeaders = String.join(";", headersToSign.keySet());
86 signedHeaders = signedHeaders.trim().toLowerCase();
87
88 String canonicalRequest =
89 method + "\n" + canonicalURI + "\n" + canonicalQueryString + "\n" + canonicalHeader;
90
91 System.out.println("authString :" + authString);
92 System.out.println("signingKey :" + signingKey);
93 System.out.println("canonicalRequest: " + canonicalRequest);
94
95 // Signing the canonical request using key with sha-256 algorithm.
96 String signature = sha256Hex(signingKey, canonicalRequest);
97
98 return authString + "/" + signedHeaders + "/" + signature;
99 }
100
101
102 private static String sha256Hex(String signingKey, String stringToSign) {
103 try {
104 Mac mac = Mac.getInstance("HmacSHA256");
105 mac.init(new SecretKeySpec(signingKey.getBytes(UTF8), "HmacSHA256"));
106 return bytesToHex(mac.doFinal(stringToSign.getBytes(UTF8)));
107 } catch (Exception e) {
108 throw new RuntimeException("Fail to generate the signature", e);
109 }
110 }
111
112 private static String bytesToHex(byte[] bytes) {
113 StringBuilder result = new StringBuilder();
114 for (byte b : bytes) {
115 result.append(String.format("%02x", b));
116 }
117 return result.toString();
118 }
119
120 private static String getCanonicalURIPath(String path) {
121 if (path == null) {
122 return "/";
123 } else if (path.startsWith("/")) {
124 return normalizePath(path);
125 } else {
126 return "/" + normalizePath(path);
127 }
128 }
129
130 private static String normalize(String value) {
131 try {
132 StringBuilder builder = new StringBuilder();
133 for (byte b : value.getBytes(DEFAULT_ENCODING)) {
134 if (URI_UNRESERVED_CHARACTERS.get(b & 0xFF)) {
135 builder.append((char) b);
136 } else {
137 builder.append(PERCENT_ENCODED_STRINGS[b & 0xFF]);
138 }
139 }
140 return builder.toString();
141 } catch (UnsupportedEncodingException e) {
142 throw new RuntimeException(e);
143 }
144 }
145
146 private static String normalizePath(String path) {
147 return normalize(path).replace("%2F", "/");
148 }
149
150
151 private static String getCanonicalQueryString(Map<String, String> parameters, boolean forSignature) {
152 if (parameters.isEmpty()) {
153 return "";
154 }
155
156 List<String> parameterStrings = new ArrayList<>();
157 for (Map.Entry<String, String> entry : parameters.entrySet()) {
158 if (forSignature && AUTHORIZATION.equalsIgnoreCase(entry.getKey())) {
159 continue;
160 }
161 String key = entry.getKey();
162 checkNotNull(key, "parameter key should not be null");
163 String value = entry.getValue();
164 if (value == null) {
165 if (forSignature) {
166 parameterStrings.add(normalize(key) + '=');
167 } else {
168 parameterStrings.add(normalize(key));
169 }
170 } else {
171 parameterStrings.add(normalize(key) + '=' + normalize(value));
172 }
173 }
174 Collections.sort(parameterStrings);
175
176 return String.join("&", parameterStrings);
177 }
178
179 private static <T> T checkNotNull(T reference, Object errorMessage) {
180 if (reference == null) {
181 throw new NullPointerException(String.valueOf(errorMessage));
182 } else {
183 return reference;
184 }
185 }
186
187 private static SortedMap<String, String> getHeadersToSign(Map<String, String> headers, Set<String> headersToSign) {
188 SortedMap<String, String> ret = new TreeMap<>();
189 if (headersToSign != null) {
190 Set<String> tempSet = new HashSet<>();
191 for (String header : headersToSign) {
192 tempSet.add(header.trim().toLowerCase());
193 }
194 headersToSign = tempSet;
195 }
196 for (Map.Entry<String, String> entry : headers.entrySet()) {
197 String key = entry.getKey();
198 if (entry.getValue() != null && !entry.getValue().isEmpty()) {
199 if ((headersToSign == null && isDefaultHeaderToSign(key))
200 || (headersToSign != null && headersToSign.contains(key.toLowerCase())
201 && !AUTHORIZATION.equalsIgnoreCase(key))) {
202 ret.put(key, entry.getValue());
203 }
204 }
205 }
206 return ret;
207 }
208
209 private static boolean isDefaultHeaderToSign(String header) {
210 header = header.trim().toLowerCase();
211 return header.startsWith(BCE_PREFIX) || defaultHeadersToSign.contains(header);
212 }
213
214 private static String getCanonicalHeaders(SortedMap<String, String> headers) {
215 if (headers.isEmpty()) {
216 return "";
217 }
218
219 List<String> headerStrings = new ArrayList<>();
220 for (Map.Entry<String, String> entry : headers.entrySet()) {
221 String key = entry.getKey();
222 if (key == null) {
223 continue;
224 }
225 String value = entry.getValue();
226 if (value == null) {
227 value = "";
228 }
229 headerStrings.add(normalize(key.trim().toLowerCase()) + ':' + normalize(value.trim()));
230 }
231 Collections.sort(headerStrings);
232
233 return String.join("\n", headerStrings);
234 }
235
236}
测试代码
Java
1import org.apache.http.HttpEntity;
2import org.apache.http.HttpHeaders;
3import org.apache.http.HttpResponse;
4import org.apache.http.client.methods.HttpPost;
5import org.apache.http.entity.StringEntity;
6import org.apache.http.impl.client.CloseableHttpClient;
7import org.apache.http.impl.client.HttpClients;
8import org.apache.http.util.EntityUtils;
9import org.junit.Test;
10
11import java.util.HashMap;
12import java.util.Map;
13
14
15public class BaiduV1SingerTest {
16 private static final String CONTENT_TYPE = "application/json";
17
18
19 @Test
20 public void postApiDemo() {
21
22
23 String host = "smarthome-bdvs.baidubce.com";
24 String uri = "/v1/dynamicDict/list";
25
26 String url = "https://" + host + uri;
27
28
29 // todo: 请求体
30 String requestBodyStr = "{\n" +
31 " \"fc\": \"device_fc\",\n" +
32 " \"pk\": \"device_pk\",\n" +
33 " \"ak\": \"mock_ak02\",\n" +
34 " \"normValue\": \"A01\",\n" +
35 " \"synonymValue\": \"xxxx" +
36 "\"\n" +
37 "}";
38
39 Map<String, String> hashMap = new HashMap<>();
40 hashMap.put(HttpHeaders.CONTENT_TYPE, "application/json");
41 hashMap.put(HttpHeaders.HOST, host);
42
43 // 创建一个HttpClient对象
44 try (CloseableHttpClient httpclient = HttpClients.createDefault()) {
45 // 创建一个HttpGet请求
46 HttpPost httpPost = new HttpPost(url);
47 System.out.println("Executing request " + httpPost.getRequestLine());
48
49 // 进行百度请求签名
50// AwsApacheHttpClientSigner.getSignedRequest(httpPost, requestBodyStr);
51 httpPost.setEntity(new StringEntity(requestBodyStr));
52 httpPost.addHeader(HttpHeaders.CONTENT_TYPE, "application/json");
53 httpPost.addHeader(HttpHeaders.HOST, host);
54 httpPost.addHeader(HttpHeaders.AUTHORIZATION, BaiduV1Singer.sign(uri, "POST", new HashMap<>(), hashMap));
55
56 // 执行请求并获取HttpResponse对象
57 HttpResponse httpResponse = httpclient.execute(httpPost);
58 HttpEntity entity = httpResponse.getEntity();
59
60 if (entity != null) {
61 // 打印响应内容
62 System.out.println("Response content: " + EntityUtils.toString(entity));
63 }
64 } catch (Exception e) {
65 throw new RuntimeException(e);
66 }
67 // 关闭HttpClient连接
68 }
69
70
71}
AWS版本
核心代码
Java
1import org.apache.http.HttpHeaders;
2import org.apache.http.client.methods.HttpPost;
3import org.apache.http.entity.StringEntity;
4
5import javax.crypto.Mac;
6import javax.crypto.spec.SecretKeySpec;
7import java.net.URI;
8import java.net.URISyntaxException;
9import java.nio.charset.StandardCharsets;
10import java.security.MessageDigest;
11import java.security.NoSuchAlgorithmException;
12import java.time.LocalDateTime;
13import java.time.ZoneId;
14import java.time.format.DateTimeFormatter;
15
16public class AwsApacheHttpClientSigner {
17 !!!替换为百度智能云账户下的ak和sk
18 private static final String ACCESS_KEY = "your_ak";
19 private static final String SECRET_KEY = "your_sk";
20
21 private static final String CONTENT_TYPE = "application/json";
22
23 /**
24 * 固定值
25 */
26 private static final String REGION = "us-east-1";
27
28 /**
29 * 固定值
30 */
31 private static final String SERVICE = "execute-api";
32
33
34 /**
35 * 生成带有签名的请求
36 *
37 * @param httpPost 需要签名的请求
38 * @param requestBody 请求体
39 * @return 带有签名的请求
40 * @throws Exception 签名过程中可能发生的异常
41 */
42 public static void getSignedRequest(HttpPost httpPost, String requestBody) throws Exception {
43 String format = getAuthDate();
44 String authorizationHeader = getAuthorizationHeader(httpPost.getURI().toString(), httpPost.getMethod(), requestBody,
45 format);
46 httpPost.addHeader(HttpHeaders.CONTENT_TYPE, CONTENT_TYPE);
47 httpPost.addHeader(HttpHeaders.AUTHORIZATION, authorizationHeader);
48 httpPost.addHeader("X-Amz-Content-Sha256", calculateSHA256(requestBody));
49 httpPost.addHeader("X-Amz-Date", format);
50 httpPost.setEntity(new StringEntity(requestBody));
51 }
52
53
54 private static String getAuthDate() {
55 return LocalDateTime.now(ZoneId.of("UTC")).format(DateTimeFormatter.ofPattern("yyyyMMdd'T'HHmmss'Z'"));
56 }
57
58 public static String getAuthorizationHeader(String url, String method, String requestBody, String amzDate) throws Exception {
59
60 URI uri;
61 try {
62 uri = new URI(url);
63 } catch (URISyntaxException e) {
64 throw new Exception("Invalid URL: " + url);
65 }
66 String host = uri.getAuthority();
67 String canonicalUri = uri.getPath();
68 String canonicalQuerystring = uri.getQuery();
69 String contentHash = calculateSHA256(requestBody);
70
71 String canonicalHeaders = "content-type:" + CONTENT_TYPE + "\n" +
72 "host:" + host + "\n" +
73 "x-amz-content-sha256:" + contentHash + "\n" +
74 "x-amz-date:" + amzDate + "\n";
75
76 String signedHeaders = "content-type;host;x-amz-content-sha256;x-amz-date";
77 String canonicalRequest = method + "\n" + canonicalUri + "\n" + (canonicalQuerystring == null ? "" : canonicalQuerystring) + "\n" +
78 canonicalHeaders + "\n" + signedHeaders + "\n" + contentHash;
79 String credentialScope = amzDate.substring(0, 8) + "/" + REGION + "/" + SERVICE + "/aws4_request";
80 String stringToSign = "AWS4-HMAC-SHA256\n" + amzDate + "\n" + credentialScope + "\n" +
81 calculateSHA256(canonicalRequest);
82
83 byte[] signingKey = getSigningKey(amzDate.substring(0, 8));
84 String signature = calculateHMAC(stringToSign, signingKey);
85
86 return "AWS4-HMAC-SHA256 Credential=" + ACCESS_KEY + "/" + credentialScope + ", " +
87 "SignedHeaders=" + signedHeaders + ", " + "Signature=" + signature;
88 }
89
90 private static String calculateSHA256(String text) throws NoSuchAlgorithmException {
91 MessageDigest digest = MessageDigest.getInstance("SHA-256");
92 byte[] hash = digest.digest(text.getBytes(StandardCharsets.UTF_8));
93 return bytesToHex(hash);
94 }
95
96 private static String bytesToHex(byte[] bytes) {
97 StringBuilder result = new StringBuilder();
98 for (byte b : bytes) {
99 result.append(String.format("%02x", b));
100 }
101 return result.toString();
102 }
103
104 private static byte[] getSigningKey(String dateStamp) throws Exception {
105 byte[] kSecret = ("AWS4" + SECRET_KEY).getBytes(StandardCharsets.UTF_8);
106 byte[] kDate = hmacSHA256(dateStamp, kSecret);
107 byte[] kRegion = hmacSHA256(REGION, kDate);
108 byte[] kService = hmacSHA256(SERVICE, kRegion);
109 return hmacSHA256("aws4_request", kService);
110 }
111
112 private static byte[] hmacSHA256(String data, byte[] key) throws Exception {
113 Mac mac = Mac.getInstance("HmacSHA256");
114 SecretKeySpec keySpec = new SecretKeySpec(key, "HmacSHA256");
115 mac.init(keySpec);
116 return mac.doFinal(data.getBytes(StandardCharsets.UTF_8));
117 }
118
119 private static String calculateHMAC(String data, byte[] key) throws Exception {
120 byte[] hmacData = hmacSHA256(data, key);
121 return bytesToHex(hmacData);
122 }
123
124
125}
Java
11. 名称标识:用于区分不同用户,在sdk包中通过该字段获取对应的库文件,可使用字母和数字,不可以包含其他特殊字符,建议增加特定字段避免重复
22. 编译链工具压缩包:用于进行编译sdk包
33. 解压命令:解压编译链压缩包的命令,例如"tar -xvzf"
44. 编译链前缀:编译链所在的路径前缀,例如"bin/arm-linux-gnueabihf-"
55. 编译选项:如果编译链使用时需要增加额外的编译选项(CFLAGS),请提供
测试代码
Java
1import org.apache.http.HttpEntity;
2import org.apache.http.HttpResponse;
3import org.apache.http.client.methods.HttpPost;
4import org.apache.http.impl.client.CloseableHttpClient;
5import org.apache.http.impl.client.HttpClients;
6import org.apache.http.util.EntityUtils;
7import org.junit.Test;
8
9
10public class ApacheHttpClientSignerTest {
11
12
13 @Test
14 public void postApiDemo() {
15
16 !!! 业务对应的接口地址
17 String url = "http://smarthome-bdvs.baidubce.com/v1/dynamicDict/list";
18
19 !!! todo: 请求体
20 String requestBodyStr = "{\n" +
21 " \"fc\": \"device_fc\",\n" +
22 " \"pk\": \"device_pk\",\n" +
23 " \"ak\": \"mock_ak01\",\n" +
24 " \"normValue\": \"A01\",\n" +
25 " \"synonymValue\": \"xxxx\"\n" +
26 "}";
27
28
29 // 创建一个HttpClient对象
30 try (CloseableHttpClient httpclient = HttpClients.createDefault()) {
31 // 创建一个HttpGet请求
32 HttpPost httpPost = new HttpPost(url);
33 System.out.println("Executing request " + httpPost.getRequestLine());
34
35 // 进行百度请求签名
36 AwsApacheHttpClientSigner.getSignedRequest(httpPost, requestBodyStr);
37
38 // 执行请求并获取HttpResponse对象
39 HttpResponse httpResponse = httpclient.execute(httpPost);
40 HttpEntity entity = httpResponse.getEntity();
41
42 if (entity != null) {
43 // 打印响应内容
44 System.out.println("Response content: " + EntityUtils.toString(entity));
45 }
46 } catch (Exception e) {
47 throw new RuntimeException(e);
48 }
49 // 关闭HttpClient连接
50 }
51
52}
baiduV1版本(推荐)
核心代码
Java
1import javax.crypto.Mac;
2import javax.crypto.spec.SecretKeySpec;
3import java.io.UnsupportedEncodingException;
4import java.nio.charset.Charset;
5import java.time.LocalDateTime;
6import java.time.ZoneId;
7import java.util.ArrayList;
8import java.util.BitSet;
9import java.util.Collections;
10import java.util.HashSet;
11import java.util.List;
12import java.util.Map;
13import java.util.Set;
14import java.util.SortedMap;
15import java.util.TreeMap;
16
17
18public class BaiduV1Singer {
19
20 // todo 修改成自己的ak和sk
21 private static final String ACCESS_KEY = "your_ak";
22 private static final String SECRET_KEY = "your_sk";
23
24
25 public static final String HOST = "Host";
26 public static final String AUTHORIZATION = "Authorization";
27 public static final String CONTENT_LENGTH = "Content-Length";
28 public static final String CONTENT_MD5 = "Content-MD5";
29 public static final String CONTENT_TYPE = "Content-Type";
30 public static final String BCE_PREFIX = "x-bce-";
31
32 private static final String BCE_AUTH_VERSION = "bce-auth-v1";
33 private static final String DEFAULT_ENCODING = "UTF-8";
34 private static final Charset UTF8 = Charset.forName(DEFAULT_ENCODING);
35 private static final BitSet URI_UNRESERVED_CHARACTERS = new BitSet();
36 private static final String[] PERCENT_ENCODED_STRINGS = new String[256];
37 private static final Set<String> defaultHeadersToSign = new HashSet<>();
38 private static final java.time.format.DateTimeFormatter formatter =
39 java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss'Z'");
40
41
42 static {
43 defaultHeadersToSign.add(HOST.toLowerCase());
44 defaultHeadersToSign.add(CONTENT_LENGTH.toLowerCase());
45 defaultHeadersToSign.add(CONTENT_TYPE.toLowerCase());
46 defaultHeadersToSign.add(CONTENT_MD5.toLowerCase());
47 }
48
49 static {
50 for (int i = 'a'; i <= 'z'; i++) {
51 URI_UNRESERVED_CHARACTERS.set(i);
52 }
53 for (int i = 'A'; i <= 'Z'; i++) {
54 URI_UNRESERVED_CHARACTERS.set(i);
55 }
56 for (int i = '0'; i <= '9'; i++) {
57 URI_UNRESERVED_CHARACTERS.set(i);
58 }
59 URI_UNRESERVED_CHARACTERS.set('-');
60 URI_UNRESERVED_CHARACTERS.set('.');
61 URI_UNRESERVED_CHARACTERS.set('_');
62 URI_UNRESERVED_CHARACTERS.set('~');
63
64 for (int i = 0; i < PERCENT_ENCODED_STRINGS.length; ++i) {
65 PERCENT_ENCODED_STRINGS[i] = String.format("%%%02X", i);
66 }
67 }
68
69 public static String sign(String uri, String method, Map<String, String> parameters, Map<String, String> headers) {
70
71 String authString =
72 BCE_AUTH_VERSION + "/" + ACCESS_KEY + "/"
73 + LocalDateTime.now(ZoneId.of("UTC")).format(formatter) + "/" + 1800;
74 String signingKey = sha256Hex(SECRET_KEY, authString);
75
76 String canonicalURI = getCanonicalURIPath(uri);
77
78 String canonicalQueryString = getCanonicalQueryString(parameters, true);
79
80 SortedMap<String, String> headersToSign = getHeadersToSign(headers, null);
81
82 String canonicalHeader = getCanonicalHeaders(headersToSign);
83
84 String signedHeaders;
85 signedHeaders = String.join(";", headersToSign.keySet());
86 signedHeaders = signedHeaders.trim().toLowerCase();
87
88 String canonicalRequest =
89 method + "\n" + canonicalURI + "\n" + canonicalQueryString + "\n" + canonicalHeader;
90
91 System.out.println("authString :" + authString);
92 System.out.println("signingKey :" + signingKey);
93 System.out.println("canonicalRequest: " + canonicalRequest);
94
95 // Signing the canonical request using key with sha-256 algorithm.
96 String signature = sha256Hex(signingKey, canonicalRequest);
97
98 return authString + "/" + signedHeaders + "/" + signature;
99 }
100
101
102 private static String sha256Hex(String signingKey, String stringToSign) {
103 try {
104 Mac mac = Mac.getInstance("HmacSHA256");
105 mac.init(new SecretKeySpec(signingKey.getBytes(UTF8), "HmacSHA256"));
106 return bytesToHex(mac.doFinal(stringToSign.getBytes(UTF8)));
107 } catch (Exception e) {
108 throw new RuntimeException("Fail to generate the signature", e);
109 }
110 }
111
112 private static String bytesToHex(byte[] bytes) {
113 StringBuilder result = new StringBuilder();
114 for (byte b : bytes) {
115 result.append(String.format("%02x", b));
116 }
117 return result.toString();
118 }
119
120 private static String getCanonicalURIPath(String path) {
121 if (path == null) {
122 return "/";
123 } else if (path.startsWith("/")) {
124 return normalizePath(path);
125 } else {
126 return "/" + normalizePath(path);
127 }
128 }
129
130 private static String normalize(String value) {
131 try {
132 StringBuilder builder = new StringBuilder();
133 for (byte b : value.getBytes(DEFAULT_ENCODING)) {
134 if (URI_UNRESERVED_CHARACTERS.get(b & 0xFF)) {
135 builder.append((char) b);
136 } else {
137 builder.append(PERCENT_ENCODED_STRINGS[b & 0xFF]);
138 }
139 }
140 return builder.toString();
141 } catch (UnsupportedEncodingException e) {
142 throw new RuntimeException(e);
143 }
144 }
145
146 private static String normalizePath(String path) {
147 return normalize(path).replace("%2F", "/");
148 }
149
150
151 private static String getCanonicalQueryString(Map<String, String> parameters, boolean forSignature) {
152 if (parameters.isEmpty()) {
153 return "";
154 }
155
156 List<String> parameterStrings = new ArrayList<>();
157 for (Map.Entry<String, String> entry : parameters.entrySet()) {
158 if (forSignature && AUTHORIZATION.equalsIgnoreCase(entry.getKey())) {
159 continue;
160 }
161 String key = entry.getKey();
162 checkNotNull(key, "parameter key should not be null");
163 String value = entry.getValue();
164 if (value == null) {
165 if (forSignature) {
166 parameterStrings.add(normalize(key) + '=');
167 } else {
168 parameterStrings.add(normalize(key));
169 }
170 } else {
171 parameterStrings.add(normalize(key) + '=' + normalize(value));
172 }
173 }
174 Collections.sort(parameterStrings);
175
176 return String.join("&", parameterStrings);
177 }
178
179 private static <T> T checkNotNull(T reference, Object errorMessage) {
180 if (reference == null) {
181 throw new NullPointerException(String.valueOf(errorMessage));
182 } else {
183 return reference;
184 }
185 }
186
187 private static SortedMap<String, String> getHeadersToSign(Map<String, String> headers, Set<String> headersToSign) {
188 SortedMap<String, String> ret = new TreeMap<>();
189 if (headersToSign != null) {
190 Set<String> tempSet = new HashSet<>();
191 for (String header : headersToSign) {
192 tempSet.add(header.trim().toLowerCase());
193 }
194 headersToSign = tempSet;
195 }
196 for (Map.Entry<String, String> entry : headers.entrySet()) {
197 String key = entry.getKey();
198 if (entry.getValue() != null && !entry.getValue().isEmpty()) {
199 if ((headersToSign == null && isDefaultHeaderToSign(key))
200 || (headersToSign != null && headersToSign.contains(key.toLowerCase())
201 && !AUTHORIZATION.equalsIgnoreCase(key))) {
202 ret.put(key, entry.getValue());
203 }
204 }
205 }
206 return ret;
207 }
208
209 private static boolean isDefaultHeaderToSign(String header) {
210 header = header.trim().toLowerCase();
211 return header.startsWith(BCE_PREFIX) || defaultHeadersToSign.contains(header);
212 }
213
214 private static String getCanonicalHeaders(SortedMap<String, String> headers) {
215 if (headers.isEmpty()) {
216 return "";
217 }
218
219 List<String> headerStrings = new ArrayList<>();
220 for (Map.Entry<String, String> entry : headers.entrySet()) {
221 String key = entry.getKey();
222 if (key == null) {
223 continue;
224 }
225 String value = entry.getValue();
226 if (value == null) {
227 value = "";
228 }
229 headerStrings.add(normalize(key.trim().toLowerCase()) + ':' + normalize(value.trim()));
230 }
231 Collections.sort(headerStrings);
232
233 return String.join("\n", headerStrings);
234 }
235
236}
测试代码
Java
1import org.junit.Test;
2
3import java.io.BufferedReader;
4import java.io.DataOutputStream;
5import java.io.InputStreamReader;
6import java.net.HttpURLConnection;
7import java.net.URL;
8import java.util.HashMap;
9import java.util.Map;
10
11public class BaiduV1SignerTest {
12
13 @Test
14 public void postApiTest() throws Exception {
15
16 // todo: host
17 String host = "smarthome-bdvs.baidubce.com";
18
19
20 // todo: 请求地址
21 String uri = "/v1/dynamicDict/list";
22
23
24 // todo: 请求体
25 String requestBodyStr = "{\n" +
26 " \"fc\": \"device_fc\",\n" +
27 " \"pk\": \"device_pk\",\n" +
28 " \"ak\": \"mock_ak01\",\n" +
29 " \"normValue\": \"A01\",\n" +
30 " \"synonymValue\": \"xxxx\"\n" +
31 "}";
32
33
34 String urls = "https://" + host + uri;
35 // 创建一个URL对象
36 URL url = new URL(urls);
37
38 Map<String, String> hashMap = new HashMap<>();
39 hashMap.put("Content-Type", "application/json");
40 hashMap.put("Host", host);
41
42 // 使用URL对象创建一个URLConnection对象
43 HttpURLConnection connection = (HttpURLConnection) url.openConnection();
44 connection.setRequestMethod("POST");
45 connection.setRequestProperty("Content-Type", "application/json");
46 connection.setRequestProperty("Authorization", BaiduV1Singer.sign(uri, "POST", new HashMap<>(), hashMap));
47
48 // 发送POST请求必须设置如下两行
49 connection.setDoOutput(true);
50 connection.setDoInput(true);
51
52 // 获取输出流
53 DataOutputStream out = new DataOutputStream(connection.getOutputStream());
54 out.writeBytes(requestBodyStr);
55 out.flush();
56 out.close();
57
58 // 从URLConnection对象获取输入流
59 BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
60
61 String inputLine;
62 while ((inputLine = in.readLine()) != null)
63 System.out.println(inputLine);
64 in.close();
65 }
66
67}
AWS版本
核心代码
Java
1import javax.crypto.Mac;
2import javax.crypto.spec.SecretKeySpec;
3import java.net.HttpURLConnection;
4import java.net.URI;~~~~
5import java.net.URISyntaxException;
6import java.nio.charset.StandardCharsets;
7import java.security.MessageDigest;
8import java.security.NoSuchAlgorithmException;
9import java.time.LocalDateTime;
10import java.time.ZoneId;
11import java.time.format.DateTimeFormatter;
12
13public class BaiduUrlConnectionSigner {
14
15 private static final String ACCESS_KEY = "your_ak";
16 private static final String SECRET_KEY = "your_sk";
17
18
19 private static final String CONTENT_TYPE = "application/json";
20
21 /**
22 * 固定值
23 */
24 private static final String REGION = "us-east-1";
25
26 /**
27 * 固定值
28 */
29 private static final String SERVICE = "execute-api";
30
31
32 /**
33 * 生成带有签名的请求
34 *
35 * @param connection 需要签名的请求
36 * @param requestBody 请求体
37 * @return 带有签名的请求
38 * @throws Exception 签名过程中可能发生的异常
39 */
40 public static void getSignedRequest(HttpURLConnection connection, String requestBody) throws Exception {
41 String format = getAuthDate();
42 String authorizationHeader = getAuthorizationHeader(connection.getURL().toString(), connection.getRequestMethod(), requestBody,
43 format);
44 connection.setRequestProperty("Content-Type", CONTENT_TYPE);
45 connection.setRequestProperty("Authorization", authorizationHeader);
46 connection.setRequestProperty("X-Amz-Content-Sha256", calculateSHA256(requestBody));
47 connection.setRequestProperty("X-Amz-Date", format);
48 }
49
50
51 private static String getAuthDate() {
52 return LocalDateTime.now(ZoneId.of("UTC")).format(DateTimeFormatter.ofPattern("yyyyMMdd'T'HHmmss'Z'"));
53 }
54
55 public static String getAuthorizationHeader(String url, String method, String requestBody, String amzDate) throws Exception {
56
57 URI uri;
58 try {
59 uri = new URI(url);
60 } catch (URISyntaxException e) {
61 throw new Exception("Invalid URL: " + url);
62 }
63 String host = uri.getAuthority();
64 String canonicalUri = uri.getPath();
65 String canonicalQuerystring = uri.getQuery();
66 String contentHash = calculateSHA256(requestBody);
67
68 String canonicalHeaders = "content-type:" + CONTENT_TYPE + "\n" +
69 "host:" + host + "\n" +
70 "x-amz-content-sha256:" + contentHash + "\n" +
71 "x-amz-date:" + amzDate + "\n";
72
73 String signedHeaders = "content-type;host;x-amz-content-sha256;x-amz-date";
74 String canonicalRequest = method + "\n" + canonicalUri + "\n" + (canonicalQuerystring == null ? "" : canonicalQuerystring) + "\n" +
75 canonicalHeaders + "\n" + signedHeaders + "\n" + contentHash;
76 String credentialScope = amzDate.substring(0, 8) + "/" + REGION + "/" + SERVICE + "/aws4_request";
77 String stringToSign = "AWS4-HMAC-SHA256\n" + amzDate + "\n" + credentialScope + "\n" +
78 calculateSHA256(canonicalRequest);
79
80 byte[] signingKey = getSigningKey(amzDate.substring(0, 8));
81 String signature = calculateHMAC(stringToSign, signingKey);
82
83 return "AWS4-HMAC-SHA256 Credential=" + ACCESS_KEY + "/" + credentialScope + ", " +
84 "SignedHeaders=" + signedHeaders + ", " + "Signature=" + signature;
85 }
86
87 private static String calculateSHA256(String text) throws NoSuchAlgorithmException {
88 MessageDigest digest = MessageDigest.getInstance("SHA-256");
89 byte[] hash = digest.digest(text.getBytes(StandardCharsets.UTF_8));
90 return bytesToHex(hash);
91 }
92
93 private static String bytesToHex(byte[] bytes) {
94 StringBuilder result = new StringBuilder();
95 for (byte b : bytes) {
96 result.append(String.format("%02x", b));
97 }
98 return result.toString();
99 }
100
101 private static byte[] getSigningKey(String dateStamp) throws Exception {
102 byte[] kSecret = ("AWS4" + SECRET_KEY).getBytes(StandardCharsets.UTF_8);
103 byte[] kDate = hmacSHA256(dateStamp, kSecret);
104 byte[] kRegion = hmacSHA256(REGION, kDate);
105 byte[] kService = hmacSHA256(SERVICE, kRegion);
106 return hmacSHA256("aws4_request", kService);
107 }
108
109 private static byte[] hmacSHA256(String data, byte[] key) throws Exception {
110 Mac mac = Mac.getInstance("HmacSHA256");
111 SecretKeySpec keySpec = new SecretKeySpec(key, "HmacSHA256");
112 mac.init(keySpec);
113 return mac.doFinal(data.getBytes(StandardCharsets.UTF_8));
114 }
115
116 private static String calculateHMAC(String data, byte[] key) throws Exception {
117 byte[] hmacData = hmacSHA256(data, key);
118 return bytesToHex(hmacData);
119 }
120
121
122}
测试代码
Java
1import org.junit.Test;
2
3import java.io.BufferedReader;
4import java.io.DataOutputStream;
5import java.io.InputStreamReader;
6import java.net.HttpURLConnection;
7import java.net.URL;
8
9public class URLConnectionSignerTest {
10
11 @Test
12 public void postApiTest() throws Exception {
13
14 // todo: 请求地址
15 String urls = "http://smarthome-bdvs.baidubce.com/v1/dynamicDict/list";
16
17 // todo: 请求体
18 String requestBodyStr = "{\n" +
19 " \"fc\": \"device_fc\",\n" +
20 " \"pk\": \"device_pk\",\n" +
21 " \"ak\": \"mock_ak01\",\n" +
22 " \"normValue\": \"A01\",\n" +
23 " \"synonymValue\": \"xxxx\"\n" +
24 "}";
25
26 // 创建一个URL对象
27 URL url = new URL(urls);
28
29 // 使用URL对象创建一个URLConnection对象
30 HttpURLConnection connection = (HttpURLConnection) url.openConnection();
31 connection.setRequestMethod("POST");
32
33 // 设置请求签名。
34 BaiduUrlConnectionSigner.getSignedRequest(connection, requestBodyStr);
35
36 // 发送POST请求必须设置如下两行
37 connection.setDoOutput(true);
38 connection.setDoInput(true);
39
40 // 获取输出流
41 DataOutputStream out = new DataOutputStream(connection.getOutputStream());
42 out.writeBytes(requestBodyStr);
43 out.flush();
44 out.close();
45
46 // 从URLConnection对象获取输入流
47 BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
48
49 String inputLine;
50 while ((inputLine = in.readLine()) != null)
51 System.out.println(inputLine);
52 in.close();
53 }
54
55}