如何签名(Presign)一个AWS S3文件

邵锐
2023-12-01

我用的 ServiceAccount 关联的 IAM Role 做的 k8s 应用内的授权。使用的 aws-java-sdk v1 的版本客户端。 这样 Presign 一个 S3 文件:

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.Date;

import com.amazonaws.SDKGlobalConfiguration;
import com.amazonaws.auth.WebIdentityTokenCredentialsProvider;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
import com.amazonaws.services.s3.model.GeneratePresignedUrlRequest;

public class S3Signer{
    static{
        System.setProperty(SDKGlobalConfiguration.ENABLE_S3_SIGV4_SYSTEM_PROPERTY, "true");
    }

    public static String generateUrl(AmazonS3 amazonS3, String bucketName, String key) {
        GeneratePresignedUrlRequest urlRequest = new GeneratePresignedUrlRequest(bucketName,key);
        // 1 hour expiration time
        urlRequest.setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60));
        URL url = amazonS3.generatePresignedUrl(urlRequest);

        return url.toString();
    }

    public static void main(String[] args) {
        String region = args[0];
        String roleArn = args[1];
        String sessionName = args[2];
        String tokenFile = args[3];

        String bucketName = args[4];
        String key = args[5];

        AmazonS3ClientBuilder builder = AmazonS3ClientBuilder.standard().withRegion(region);

        System.out.println("roleArn:"+  roleArn + " tokenFile:" + tokenFile + " sessionName:" + sessionName);
        builder.withCredentials(WebIdentityTokenCredentialsProvider.builder()
            .roleArn(roleArn)
            .webIdentityTokenFile(tokenFile)
            .roleSessionName(sessionName)
            .build());

        AmazonS3 s3 = builder.build();

        String location = s3.getBucketLocation(bucketName);
        System.out.println("bucket location:" + location + " bucket: " + bucketName);
        s3.putObject(bucketName,"hello.txt", "hello world");

        System.out.println(generateUrl(s3, bucketName, key));

    }
}

如果你有遇到下面这个 AuthorizationQueryParametersError 错误,

<Error>

<Code>AuthorizationQueryParametersError</Code>

<Message>X-Amz-Algorithm only supports "AWS4-HMAC-SHA256 and AWS4-ECDSA-P256-SHA256"</Message>

<RequestId>EHCHR1687MYQKZZD</RequestId>

<HostId>xlywJTkA5oqp/9sA6VaVKqYwGEDKpK2AS5EK2R6DVpioNZUGJS3I97lWFAKAxNVYfHVMCJ4RDZM=</HostId>

</Error>

这个错误说明签名得到了 AWS3 的参数,算法不支持,需要使用 S4的算法。加上签名的代码:

System.setProperty(SDKGlobalConfiguration.ENABLE_S3_SIGV4_SYSTEM_PROPERTY, "true");

如果遇到下面的 UnauthorizedAccess 错误信息,提示 You are not authorized to perform this operation:

<Error>
<Code>UnauthorizedAccess</Code>
<Message>You are not authorized to perform this operation</Message>
<RequestId>MCRMH7258TQ6ATXG</RequestId>
<HostId>D7WNA1djj5eHh3oA2x6Sj7ef7ke2R3VxX4DyB+uiNsOXQHmQaLI/MzTO/mQqIQ9wWIfi3wyFY8A=</HostId>
</Error>

依次检查:

1.权限是否正确。 比如用 IAM Role 的话,检查 环境变量中是否有正确的信息

 kubectl exec  my-application-deplyoment-67f8d677f5-ztw7z env | grep AWS

比如我的信息

AWS_REGION=cn-northwest-1
AWS_ROLE_ARN=arn:aws-cn:iam::xxxx:role/my-cluster-addon-iamserviceaccount-al-Role1-abcd
AWS_WEB_IDENTITY_TOKEN_FILE=/var/run/secrets/eks.amazonaws.com/serviceaccount/token
AWS_DEFAULT_REGION=cn-northwest-1

2.检查授权策略是否包含了 S3 的访问权限。

3.如果是在中国区,默认访问不了 80, 443 端口的 URL。需要上传 ICP 备案信息之后才可以正常访问到。 参考:amazon web services - You are not authorized to perform this operation - Stack Overflow

如果读取 token 遇到错误, 参考:

https://github.com/aws/aws-sdk-java-v2/issues/1470s

使用 aws 2.0 SDK 签名 S3 URL:

1Example usage for com.amazonaws.services.s3.model GeneratePresignedUrlRequest GeneratePresignedUrlRequest

https://github.com/awsdocs/aws-doc-sdk-examples/blob/main/javav2/example_code/s3/src/main/java/com/example/s3/GetObjectPresignedUrl.java

AWS Java SDK 2.0 create a presigned URL for a S3 object | Newbedev

 

[Solved] Java Can't access S3 PreSigned URL due to authorization - Code Redirect

 类似资料: