为Google Cloud Storage创建签名URL

时间:2018-10-03 05:54:37

标签: google-cloud-storage

我们有一个在GCP上运行的ERP应用程序。 要下载超过三个月左右的数据,我们正在GCS上载文件。现在,我想创建一个签名的URL,以便为最终用户提供有限的访问权限。 我一直在尝试这个。但是我得到这个错误: 签名不匹配。请检查您的Google密钥。 谁能告诉我该怎么做?

private static final int EXPIRATION_TIME = 5;
private static final String BASE_URL = "https://storage.googleapis.com";
private static final  String httpVerb = "GET";
/*
 * private static final String BUCKET = "my_bucket"; private static final String
 * FOLDER = "folder";
 */
private final AppIdentityService identityService = AppIdentityServiceFactory.getAppIdentityService();

public String getSignedUrl(String bucket, final String fileName, String contentTpe) throws Exception {
    final long expiration = expiration();
    final String unsigned = stringToSign(bucket, expiration, fileName, contentTpe);
    final String signature = sign(unsigned);

    return new StringBuilder(BASE_URL).append("/").append(bucket).append("/").append(fileName)
            .append("?GoogleAccessId=").append(clientId()).append("&Expires=").append(expiration)
            .append("&Signature=").append(URLEncoder.encode(signature, "UTF-8")).toString();
}

private static long expiration() {
    final long unitMil = 1000l;
    final Calendar calendar = Calendar.getInstance();
    calendar.add(Calendar.MINUTE, EXPIRATION_TIME);
    final long expiration = calendar.getTimeInMillis() / unitMil;
    return expiration;
}

private String stringToSign(String bucket, final long expiration, String filename, String contentType) {
    final String contentMD5 = "";
    final String canonicalizedExtensionHeaders = "";
    final String canonicalizedResource = "/" + bucket + "/" + filename;      
    final String stringToSign = httpVerb + "\n"+ contentMD5 + "\n" + contentType + "\n" + expiration + "\n"
            + canonicalizedExtensionHeaders + canonicalizedResource;
    return stringToSign;
}

protected String sign(final String stringToSign) throws UnsupportedEncodingException {
    final SigningResult signingResult = identityService.signForApp(stringToSign.getBytes());
    final String encodedSignature = new String(Base64.encodeBase64(signingResult.getSignature()), "UTF-8");
    return encodedSignature;
}

protected String clientId() {
    return identityService.getServiceAccountName();
}

1 个答案:

答案 0 :(得分:0)

URL签名代码有些棘手,因为从本质上讲,除了仅仅看到错误之外,很难知道您犯了什么错误。有一些通用的技巧可以使您更轻松:

首先,如果可能的话,请考虑在google-cloud库中使用URL签名功能。例如,Java google-cloud library提供了一个Storage.signURL方法,您可以像这样使用它:

URL signedUrl = storage.signUrl(
    BlobInfo.newBuilder(bucketName, blobName).build(),
    2, TimeUnit.DAYS);

第二,如果您查看错误消息,您会注意到其中有一个<StringToSign>部分。本部分包含GCS将为其计算签名的确切字符串。确保您要签名的字符串与该字符串完全匹配。如果没有,那是你的问题。

在您的代码的特殊情况下,我没有发现问题,但可能是在对字符串进行签名时您包括了内容类型行,但是GET请求未提供Content-Type标头。不过,这只是一个主意,因为我看不到您对getSignedUrl的调用。