Skip to content

Commit a98089c

Browse files
authored
Merge pull request #542 from YangSen-qn/optimize_credential
Optimize credential
2 parents 91ac945 + d9f6c01 commit a98089c

File tree

5 files changed

+156
-73
lines changed

5 files changed

+156
-73
lines changed

src/main/java/com/qiniu/processing/OperationManager.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ public String pfop(String bucket, String key, String fops, StringMap params) thr
8989
params.put("bucket", bucket).put("key", key).put("fops", fops);
9090
byte[] data = StringUtils.utf8Bytes(params.formString());
9191
String url = configuration.apiHost(auth.accessKey, bucket) + "/pfop/";
92-
StringMap headers = auth.authorization(url, data, Client.FormMime);
92+
StringMap headers = auth.authorizationV2(url, "POST", data, Client.FormMime);
9393
Response response = client.post(url, data, headers, Client.FormMime);
9494
if (!response.isOK()) {
9595
throw new QiniuException(response);

src/main/java/com/qiniu/storage/BucketManager.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ public String[] buckets() throws QiniuException {
104104

105105
public Response bucketsResponse() throws QiniuException {
106106
String url = String.format("%s/buckets", configHelper.rsHost());
107-
Response res = get(url);
107+
Response res = post(url, null);
108108
if (!res.isOK()) {
109109
throw new QiniuException(res);
110110
}
@@ -227,7 +227,7 @@ public Response listV2(String bucket, String prefix, String marker, int limit, S
227227
throws QiniuException {
228228
String url = String.format("%s/v2/list?%s", configHelper.rsfHost(auth.accessKey, bucket),
229229
listQuery(bucket, prefix, marker, limit, delimiter));
230-
return get(url);
230+
return post(url, null);
231231
}
232232

233233
public FileListing listFilesV2(String bucket, String prefix, String marker, int limit, String delimiter)
@@ -275,7 +275,7 @@ public FileInfo stat(String bucket, String fileKey) throws QiniuException {
275275
}
276276

277277
public Response statResponse(String bucket, String fileKey) throws QiniuException {
278-
Response res = rsGet(bucket, String.format("/stat/%s", encodedEntry(bucket, fileKey)));
278+
Response res = rsPost(bucket, String.format("/stat/%s", encodedEntry(bucket, fileKey)), null);
279279
if (!res.isOK()) {
280280
throw new QiniuException(res);
281281
}
@@ -594,7 +594,7 @@ public Response asyncFetch(String url, String bucket, StringMap params) throws Q
594594
*/
595595
public Response checkAsynFetchid(String region, String fetchWorkId) throws QiniuException {
596596
String path = String.format("http://api-%s.qiniu.com/sisyphus/fetch?id=%s", region, fetchWorkId);
597-
return client.get(path, auth.authorization(path));
597+
return client.get(path, auth.authorizationV2(path));
598598
}
599599

600600
/**
@@ -1079,12 +1079,12 @@ private Response pubPost(String path) throws QiniuException {
10791079
}
10801080

10811081
private Response get(String url) throws QiniuException {
1082-
StringMap headers = auth.authorization(url);
1082+
StringMap headers = auth.authorizationV2(url, "GET", null, null);
10831083
return client.get(url, headers);
10841084
}
10851085

10861086
private Response post(String url, byte[] body) throws QiniuException {
1087-
StringMap headers = auth.authorization(url, body, Client.FormMime);
1087+
StringMap headers = auth.authorizationV2(url, "POST", body, Client.FormMime);
10881088
return client.post(url, body, headers, Client.FormMime);
10891089
}
10901090

src/main/java/com/qiniu/util/Auth.java

Lines changed: 71 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,14 @@
1515
import java.util.List;
1616

1717
public final class Auth {
18-
1918
public static final String DTOKEN_ACTION_VOD = "linking:vod";
2019
public static final String DTOKEN_ACTION_STATUS = "linking:status";
2120
public static final String DTOKEN_ACTION_TUTK = "linking:tutk";
21+
public static final String HTTP_HEADER_KEY_CONTENT_TYPE = "Content-Type";
22+
23+
private static final String QBOX_AUTHORIZATION_PREFIX = "QBox ";
24+
private static final String QINIU_AUTHORIZATION_PREFIX = "Qiniu ";
25+
2226
/**
2327
* 上传策略
2428
* 参考文档:<a href="https://developer.qiniu.com/kodo/manual/put-policy">上传策略</a>
@@ -209,19 +213,51 @@ public String signRequest(String urlString, byte[] body, String contentType) {
209213
}
210214

211215
/**
212-
* 验证回调签名是否正确
216+
* 验证回调签名是否正确,此方法仅能验证 QBox 签名以及 GET 请求的 Qiniu 签名
213217
*
214-
* @param originAuthorization 待验证签名字符串,以 "QBox "作为起始字符
218+
* @param originAuthorization 待验证签名字符串,以 "QBox " 作为起始字符,GET 请求支持 "Qiniu " 开头。
215219
* @param url 回调地址
216220
* @param body 回调请求体。原始请求体,不要解析后再封装成新的请求体--可能导致签名不一致。
217-
* @param contentType 回调ContentType
221+
* @param contentType 回调 ContentType
218222
* @return
219223
*/
224+
@Deprecated
220225
public boolean isValidCallback(String originAuthorization, String url, byte[] body, String contentType) {
221-
String authorization = "QBox " + signRequest(url, body, contentType);
226+
Headers header = null;
227+
if (!StringUtils.isNullOrEmpty(contentType)) {
228+
header = new Headers.Builder()
229+
.add(HTTP_HEADER_KEY_CONTENT_TYPE, contentType)
230+
.build();
231+
}
232+
return isValidCallback(originAuthorization, new Request(url, "GET", header, body));
233+
}
234+
235+
/**
236+
* 验证回调签名是否正确,此方法支持验证 QBox 和 Qiniu 签名
237+
*
238+
* @param originAuthorization 待验证签名字符串,以 "QBox " 或 "Qiniu " 作为起始字符
239+
* @param callback callback 请求信息
240+
* @return
241+
*/
242+
public boolean isValidCallback(String originAuthorization, Request callback) {
243+
if (callback == null) {
244+
return false;
245+
}
246+
247+
String authorization = "";
248+
if (originAuthorization.startsWith(QINIU_AUTHORIZATION_PREFIX)) {
249+
authorization = QINIU_AUTHORIZATION_PREFIX + signQiniuAuthorization(callback.url, callback.method, callback.body, callback.header);
250+
} else if (originAuthorization.startsWith(QBOX_AUTHORIZATION_PREFIX)) {
251+
String contentType = null;
252+
if (callback.header != null) {
253+
contentType = callback.header.get(HTTP_HEADER_KEY_CONTENT_TYPE);
254+
}
255+
authorization = QBOX_AUTHORIZATION_PREFIX + signRequest(callback.url, callback.body, contentType);
256+
}
222257
return authorization.equals(originAuthorization);
223258
}
224259

260+
225261
/**
226262
* 下载签名
227263
*
@@ -362,7 +398,7 @@ public String signRequestV2(String url, String method, byte[] body, String conte
362398
public String signQiniuAuthorization(String url, String method, byte[] body, String contentType) {
363399
Headers headers = null;
364400
if (!StringUtils.isNullOrEmpty(contentType)) {
365-
headers = new Headers.Builder().set("Content-Type", contentType).build();
401+
headers = new Headers.Builder().set(HTTP_HEADER_KEY_CONTENT_TYPE, contentType).build();
366402
}
367403
return signQiniuAuthorization(url, method, body, headers);
368404
}
@@ -377,7 +413,7 @@ public String signQiniuAuthorization(String url, String method, byte[] body, Hea
377413
sb.append(method).append(" ").append(uri.getPath());
378414

379415
if (uri.getQuery() != null) {
380-
sb.append("?").append(uri.getQuery());
416+
sb.append("?").append(uri.getRawQuery());
381417
}
382418

383419
sb.append("\nHost: ").append(uri.getHost() != null ? uri.getHost() : "");
@@ -389,12 +425,12 @@ public String signQiniuAuthorization(String url, String method, byte[] body, Hea
389425
String contentType = null;
390426

391427
if (null != headers) {
392-
contentType = headers.get("Content-Type");
428+
contentType = headers.get(HTTP_HEADER_KEY_CONTENT_TYPE);
393429
if (contentType != null) {
394-
sb.append("\nContent-Type: ").append(contentType);
430+
sb.append("\n").append(HTTP_HEADER_KEY_CONTENT_TYPE).append(": ").append(contentType);
395431
}
396432

397-
List<Header> xQiniuheaders = genXQiniuHeader(headers);
433+
List<Header> xQiniuheaders = genXQiniuSignHeader(headers);
398434
java.util.Collections.sort(xQiniuheaders);
399435
if (xQiniuheaders.size() > 0) {
400436
for (Header h : xQiniuheaders) {
@@ -416,11 +452,13 @@ public String signQiniuAuthorization(String url, String method, byte[] body, Hea
416452
return this.accessKey + ":" + digest;
417453
}
418454

419-
private List<Header> genXQiniuHeader(Headers headers) {
455+
private List<Header> genXQiniuSignHeader(Headers headers) {
456+
420457
ArrayList<Header> hs = new ArrayList<Header>();
421458
for (String name : headers.names()) {
422459
if (name.length() > "X-Qiniu-".length() && name.startsWith("X-Qiniu-")) {
423-
for (String value : headers.values(name)) {
460+
String value = headers.get(name);
461+
if (value != null) {
424462
hs.add(new Header(canonicalMIMEHeaderKey(name), value));
425463
}
426464
}
@@ -446,7 +484,7 @@ public StringMap authorizationV2(String url, String method, byte[] body, String
446484

447485
@Deprecated
448486
public StringMap authorizationV2(String url) {
449-
return authorizationV2(url, "GET", null, null);
487+
return authorizationV2(url, "GET", null, (String) null);
450488
}
451489

452490
//连麦 RoomToken
@@ -522,4 +560,24 @@ public int compareTo(Header o) {
522560
}
523561
}
524562
}
563+
564+
public static class Request {
565+
private final String url;
566+
private final String method;
567+
private final Headers header;
568+
private final byte[] body;
569+
570+
/**
571+
* @param url 回调地址
572+
* @param method 回调请求方式
573+
* @param header 回调头,注意 Content-Type Key 字段需为:{@link Auth#HTTP_HEADER_KEY_CONTENT_TYPE},大小写敏感
574+
* @param body 回调请求体。原始请求体,不要解析后再封装成新的请求体--可能导致签名不一致。
575+
**/
576+
public Request(String url, String method, Headers header, byte[] body) {
577+
this.url = url;
578+
this.method = method;
579+
this.header = header;
580+
this.body = body;
581+
}
582+
}
525583
}

src/test/java/test/com/qiniu/storage/BucketTest.java

Lines changed: 25 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,13 @@
1818
import org.junit.jupiter.api.Test;
1919
import test.com.qiniu.ResCode;
2020
import test.com.qiniu.TestConfig;
21-
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
22-
import static org.junit.jupiter.api.Assertions.assertEquals;
23-
import static org.junit.jupiter.api.Assertions.assertNotEquals;
24-
import static org.junit.jupiter.api.Assertions.assertNotNull;
25-
import static org.junit.jupiter.api.Assertions.assertNull;
26-
import static org.junit.jupiter.api.Assertions.assertTrue;
27-
import static org.junit.jupiter.api.Assertions.fail;
21+
2822
import java.io.IOException;
2923
import java.text.SimpleDateFormat;
3024
import java.util.*;
3125

26+
import static org.junit.jupiter.api.Assertions.*;
27+
3228
public class BucketTest {
3329

3430
List<Integer> batchStatusCode = Arrays.asList(200, 298);
@@ -264,11 +260,11 @@ public void testFile(TestConfig.TestFile file, BucketManager bucketManager) thro
264260

265261
// test bucket not exits or file not exists
266262
Map<String[], Integer> entryCodeMap = new HashMap<String[], Integer>();
267-
entryCodeMap.put(new String[] { TestConfig.testBucket_z0, TestConfig.dummyKey },
263+
entryCodeMap.put(new String[]{TestConfig.testBucket_z0, TestConfig.dummyKey},
268264
TestConfig.ERROR_CODE_KEY_NOT_EXIST);
269-
entryCodeMap.put(new String[] { TestConfig.dummyBucket, TestConfig.testKey_z0 },
265+
entryCodeMap.put(new String[]{TestConfig.dummyBucket, TestConfig.testKey_z0},
270266
TestConfig.ERROR_CODE_BUCKET_NOT_EXIST);
271-
entryCodeMap.put(new String[] { TestConfig.dummyBucket, TestConfig.dummyKey },
267+
entryCodeMap.put(new String[]{TestConfig.dummyBucket, TestConfig.dummyKey},
272268
TestConfig.ERROR_CODE_BUCKET_NOT_EXIST);
273269

274270
for (Map.Entry<String[], Integer> entry : entryCodeMap.entrySet()) {
@@ -313,11 +309,11 @@ public void testFile(TestConfig.TestFile file, BucketManager bucketManager) thro
313309
});
314310

315311
Map<String[], Integer> entryCodeMap = new HashMap<String[], Integer>();
316-
entryCodeMap.put(new String[] { TestConfig.testBucket_z0, TestConfig.dummyKey },
312+
entryCodeMap.put(new String[]{TestConfig.testBucket_z0, TestConfig.dummyKey},
317313
TestConfig.ERROR_CODE_KEY_NOT_EXIST);
318-
entryCodeMap.put(new String[] { TestConfig.testBucket_z0, null }, TestConfig.ERROR_CODE_BUCKET_NOT_EXIST);
319-
entryCodeMap.put(new String[] { TestConfig.dummyBucket, null }, TestConfig.ERROR_CODE_BUCKET_NOT_EXIST);
320-
entryCodeMap.put(new String[] { TestConfig.dummyBucket, TestConfig.dummyKey },
314+
entryCodeMap.put(new String[]{TestConfig.testBucket_z0, null}, TestConfig.ERROR_CODE_BUCKET_NOT_EXIST);
315+
entryCodeMap.put(new String[]{TestConfig.dummyBucket, null}, TestConfig.ERROR_CODE_BUCKET_NOT_EXIST);
316+
entryCodeMap.put(new String[]{TestConfig.dummyBucket, TestConfig.dummyKey},
321317
TestConfig.ERROR_CODE_BUCKET_NOT_EXIST);
322318

323319
for (Map.Entry<String[], Integer> entry : entryCodeMap.entrySet()) {
@@ -558,7 +554,7 @@ public void testFile(TestConfig.TestFile file, BucketManager bucketManager) thro
558554
assertEquals(200, response.statusCode);
559555
bucketInfo = bucketManager.getBucketInfo(file.getBucketName());
560556
assertEquals(1, bucketInfo.getAntiLeechMode());
561-
assertArrayEquals((new String[] { "www.qiniu.com" }), bucketInfo.getReferWhite());
557+
assertArrayEquals((new String[]{"www.qiniu.com"}), bucketInfo.getReferWhite());
562558
assertEquals(false, bucketInfo.isNoRefer());
563559

564560
// 测试黑名单
@@ -570,7 +566,7 @@ public void testFile(TestConfig.TestFile file, BucketManager bucketManager) thro
570566
assertEquals(200, response.statusCode);
571567
bucketInfo = bucketManager.getBucketInfo(file.getBucketName());
572568
assertEquals(2, bucketInfo.getAntiLeechMode());
573-
assertArrayEquals((new String[] { "www.baidu.com" }), bucketInfo.getReferBlack());
569+
assertArrayEquals((new String[]{"www.baidu.com"}), bucketInfo.getReferBlack());
574570
assertEquals(true, bucketInfo.isNoRefer());
575571

576572
// 测试关闭
@@ -726,7 +722,7 @@ public void testFile(TestConfig.TestFile file, BucketManager bucketManager) thro
726722

727723
// 追加Event(invalid events)
728724
try {
729-
rule = new BucketEventRule("a", new String[] {}, new String[] {});
725+
rule = new BucketEventRule("a", new String[]{}, new String[]{});
730726
System.out.println(rule.asQueryString());
731727
response = bucketManager.putBucketEvent(bucket, rule);
732728
fail();
@@ -736,7 +732,7 @@ public void testFile(TestConfig.TestFile file, BucketManager bucketManager) thro
736732

737733
// 追加Event(error:callbackURL must starts with http:// or https://)
738734
try {
739-
rule = new BucketEventRule("a", new String[] { "put", "mkfile" }, new String[] {});
735+
rule = new BucketEventRule("a", new String[]{"put", "mkfile"}, new String[]{});
740736
System.out.println(rule.asQueryString());
741737
response = bucketManager.putBucketEvent(bucket, rule);
742738
fail();
@@ -746,10 +742,10 @@ public void testFile(TestConfig.TestFile file, BucketManager bucketManager) thro
746742

747743
// 追加Event
748744
rule = new BucketEventRule("a",
749-
new String[] { "put", "mkfile", "delete", "copy", "move", "append", "disable", "enable",
750-
"deleteMarkerCreate" },
751-
new String[] { "https://requestbin.fullcontact.com/1dsqext1?inspect",
752-
"https://requestbin.fullcontact.com/160bunp1?inspect" });
745+
new String[]{"put", "mkfile", "delete", "copy", "move", "append", "disable", "enable",
746+
"deleteMarkerCreate"},
747+
new String[]{"https://requestbin.fullcontact.com/1dsqext1?inspect",
748+
"https://requestbin.fullcontact.com/160bunp1?inspect"});
753749
System.out.println(rule.asQueryString());
754750
response = bucketManager.putBucketEvent(bucket, rule);
755751
assertEquals(200, response.statusCode);
@@ -801,7 +797,7 @@ public void testFile(TestConfig.TestFile file, BucketManager bucketManager) thro
801797
// 更新Event
802798
rule.setName("b");
803799
rule.setPrefix("c");
804-
rule.setEvents(new String[] { "disable", "enable", "deleteMarkerCreate" });
800+
rule.setEvents(new String[]{"disable", "enable", "deleteMarkerCreate"});
805801
System.out.println(rule.asQueryString());
806802
response = bucketManager.updateBucketEvent(bucket, rule);
807803
assertEquals(200, response.statusCode);
@@ -848,9 +844,9 @@ public void testCorsRules() throws Exception {
848844
@Override
849845
public void testFile(TestConfig.TestFile file, BucketManager bucketManager) throws IOException {
850846
String bucket = file.getBucketName();
851-
CorsRule rule1 = new CorsRule(new String[] { "*" }, new String[] { "" });
852-
CorsRule rule2 = new CorsRule(new String[] { "*" }, new String[] { "GET", "POST" });
853-
CorsRule rule3 = new CorsRule(new String[] { "" }, new String[] { "GET", "POST" });
847+
CorsRule rule1 = new CorsRule(new String[]{"*"}, new String[]{""});
848+
CorsRule rule2 = new CorsRule(new String[]{"*"}, new String[]{"GET", "POST"});
849+
CorsRule rule3 = new CorsRule(new String[]{""}, new String[]{"GET", "POST"});
854850
List<CorsRule[]> rulesList = new ArrayList<>();
855851
rulesList.add(corsRules(rule1));
856852
rulesList.add(corsRules(rule2));
@@ -989,7 +985,7 @@ public void testPutBucketMaxAge() throws Exception {
989985
@Override
990986
public void testFile(TestConfig.TestFile file, BucketManager bucketManager) throws IOException {
991987
String bucket = file.getBucketName();
992-
final long[] maxAges = { Integer.MIN_VALUE, -54321, -1, 0, 1, 8, 1234567, 11111111, Integer.MAX_VALUE };
988+
final long[] maxAges = {Integer.MIN_VALUE, -54321, -1, 0, 1, 8, 1234567, 11111111, Integer.MAX_VALUE};
993989
try {
994990
for (long maxAge : maxAges) {
995991
// 设置max-age
@@ -1025,7 +1021,7 @@ public void testPutBucketMaxAge2() throws Exception {
10251021
public void testFile(TestConfig.TestFile file, BucketManager bucketManager) throws IOException {
10261022
String bucket = file.getBucketName();
10271023
String url = file.getTestUrl();
1028-
final long[] maxAges = { Integer.MIN_VALUE, -54321, -1, 0, 1, 8, 1234567, 11111111, Integer.MAX_VALUE };
1024+
final long[] maxAges = {Integer.MIN_VALUE, -54321, -1, 0, 1, 8, 1234567, 11111111, Integer.MAX_VALUE};
10291025
try {
10301026
for (long maxAge : maxAges) {
10311027
// 设置max-age
@@ -1425,7 +1421,7 @@ public void testBatch() throws Exception {
14251421
public void testFile(TestConfig.TestFile file, BucketManager bucketManager) throws IOException {
14261422
String bucket = file.getBucketName();
14271423
String key = file.getKey();
1428-
String[] array = { key };
1424+
String[] array = {key};
14291425
String copyFromKey = "copyFrom" + Math.random();
14301426

14311427
String moveFromKey = "moveFrom" + Math.random();

0 commit comments

Comments
 (0)