使用适用于Java的AWS开发工具包生成预签名URL

时间:2018-06-22 04:53:09

标签: amazon-web-services amazon-s3 aws-sdk aws-java-sdk pre-signed-url

我按照https://docs.aws.amazon.com/AmazonS3/latest/dev/ShareObjectPreSignedURLJavaSDK.html中的示例创建了预签名的s3网址(v4),当我尝试访问已签名的网址时,出现 Access Denied 错误

<Error>
   <Code>AccessDenied</Code>
   <Message>Access Denied</Message>
   <RequestId>0FB02ECDDF5EAC7B</RequestId>
   <HostId>vA+mmsv9PCunNe5uPkPrmpqqN3vFctQ13c9dIRlKWTYsT0zNA1V9g+4YS+lCItrBlyQtdHpyspg=</HostId>
</Error>

以下是代码段

public class GeneratePresignedURL {

    public static void main(String[] args) throws IOException {
        String clientRegion = "us-east-1";
        String bucketName = "com-example-bucket";
        String objectKey = "path/to/file.img"; // No leading `/`
        // https://com-example-bucket.s3.amazonaws.com/path/to/file.img

        try { 
            AWSCredentialsProvider awsCredentialsProvider = new DefaultAWSCredentialsProviderChain();

            // Assuming that us-east-1 defaults to v4, couldn't find a way to set it explicitly
            AmazonS3 s3Client = AmazonS3ClientBuilder.standard()
                    .withRegion(clientRegion)
                    .withCredentials(awsCredentialsProvider)
                    .build();

            // Set the presigned URL to expire after 10 minutes.
            java.util.Date expiration = new java.util.Date();
            long expTimeMillis = expiration.getTime();
            expTimeMillis += 1000 * 60 * 10;
            expiration.setTime(expTimeMillis);

            // Generate the presigned URL.
            System.out.println("Generating pre-signed URL.");
            GeneratePresignedUrlRequest generatePresignedUrlRequest = 
                    new GeneratePresignedUrlRequest(bucketName, objectKey)
                    .withMethod(HttpMethod.GET)
                    .withExpiration(expiration);
            URL url = s3Client.generatePresignedUrl(generatePresignedUrlRequest);

            System.out.println("Pre-Signed URL: " + url.toString());
            /*
                sample signature:
                https://com-example-bucket.s3.amazonaws.com/path/to/file.img?X-Amz-Security-Token=FQoDYXdzEDcaDLjUOdj2hDTZvWUQaiK3AxulqM%2BOPlp%2Bnq71P0LyuI0vj8tT%2F9i24Wd3jY8dUbudWbhUH9IAsPnl7asujO90GlaFP4dXujDDLwIakMjCJSfOFM4IoGJz8XtcjXkqJCNaenbrTA%2F3PfSl%2Fe9wQwJlY8gOu8%2Byioq2ElHULMKv52nEZj8s3v4dD0pGHQTYc4hGV7ty9CYwXNgz6w3TREhxuFdAewNgTRnY1uFNy7on6NDF5IE15vlJ2PxqrX53ZMLKP%2FdU8i5BcpZ3ySVhNpBpU3GJAPMOh%2B2ztCAk1zPjW4G0N5n9BlnjTMGs3vGBb9IW%2F8dzAoxaG9U9%2B%2FCp8euJN562dYYSZ9wmQgsfOVqc5OksdnHVkPJW400ObOcKmc9mqIRyqA%2B3Mv4z0%2Fx6iLYRJ3UaloFSGbmR6VlIxMl%2F67aHrmCnBE23a1%2BNMWgzLx%2FogqZy3CD%2F%2Fs6Jt1qkxUrRwC0RPK93LHD74qm8rjqZcEKFrBOrZsYtcl3zKgRIEHCbatQ7dwT634sdF0MwaD0vwTsbsStZDW903k5C%2FDuz4rEmkPv6c5CmFvxp4xOkUtMbDk4B8Z641CoeAMMOKICH%2FlW7%2F1as3nQo07Ow2QU%3D&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20180621T214222Z&X-Amz-SignedHeaders=host&X-Amz-Expires=3000&X-Amz-Credential=<access_key>%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Signature=<signature>
            */
        }
        catch(AmazonServiceException e) {
            // The call was transmitted successfully, but Amazon S3 couldn't process 
            // it, so it returned an error response.
            e.printStackTrace();
        }
        catch(SdkClientException e) {
            // Amazon S3 couldn't be contacted for a response, or the client
            // couldn't parse the response from Amazon S3.
            e.printStackTrace();
        }
    }
}

签名格式似乎是正确的,并且我不确定是否缺少其他一些s3客户端配置。 由于没有出现签名不匹配错误或无效的URL错误,因此我认为用于签名URL的访问密钥是正确的。

5 个答案:

答案 0 :(得分:1)

以下是生成aws-s3预签名url的步骤,以通过简单的步骤即可通过java创建存储在s3中的内容

  1. 首先在pom中添加maven依赖项
<dependency>
  <groupId>com.amazonaws</groupId>
  <artifactId>aws-java-sdk-s3</artifactId>
  <version>1.11.870</version>
</dependency>
  1. 获取所需的s3存储的S3凭证 accessKey, secretKey, region
  2. 编写Java类
  3. 首先使用提供您自己的凭据的以下代码片段获得s3Client连接
BasicAWSCredentials awsCreds = new BasicAWSCredentials(accessKey, secretKey);
final AmazonS3 s3Client = AmazonS3ClientBuilder.standard().withCredentials(new 
AWSStaticCredentialsProvider(awsCreds)).withRegion(region).build();
  1. 如果您没有创建存储分区并存储内容,则必须拥有 bucketName
  2. 使用以下代码段生成预签名的网址
// Set the expiry time
java.util.Date expiration = new java.util.Date();
long expTimeMillis = expiration.getTime();
expTimeMillis += 1000 * 60 * 60;
expiration.setTime(expTimeMillis);
  1. 传递您的objectKey注意,但是您已存在s3基本文件名
GeneratePresignedUrlRequest generatePresignedUrlRequest =
new GeneratePresignedUrlRequest(bucketName, objecKey)
.withMethod(HttpMethod.GET)
.withExpiration(expiration);
URL url = s3client.generatePresignedUrl(generatePresignedUrlRequest);
System.out.println("Pre-Signed URL: " + url.toString());

答案 1 :(得分:0)

这是由于区域不匹配。您已将clientRegion设置为 us-east-1 ,但是如果您从任何其他区域访问它,则会出现拒绝访问错误。您只能从 us-east-1 区域访问该对象,或仅将其更改为您所在的区域。对于印度客户来说是 ap-south-1

答案 2 :(得分:0)

问题似乎是该角色无权访问s3存储桶路径/到/文件

答案 3 :(得分:0)

尝试添加 .withPathStyleAccessEnabled(true),如以下快照中所述。

// Assuming that us-east-1 defaults to v4, couldn't find a way to set it explicitly
       AmazonS3 s3Client = AmazonS3ClientBuilder.standard()
                        .withRegion(clientRegion)
                        .withPathStyleAccessEnabled(true)
                        .withCredentials(awsCredentialsProvider)
                        .build();

更改/检查AWS S3存储桶的此更改CROS属性。

<CORSConfiguration>
 <CORSRule>
   <AllowedOrigin>*</AllowedOrigin>

   <AllowedMethod>PUT</AllowedMethod>
   <AllowedMethod>POST</AllowedMethod>
   <AllowedMethod>DELETE</AllowedMethod>

   <AllowedHeader>*</AllowedHeader>
 </CORSRule>
 <CORSRule>
   <AllowedOrigin>*</AllowedOrigin>
   <AllowedMethod>GET</AllowedMethod>
 </CORSRule>
</CORSConfiguration>

答案 4 :(得分:0)

请务必为Cognito身份角色分配对S3的访问权限,否则,如果您使用Cognito-credentials用户池会话提供对s3client的必需访问权限,则该权限将被拒绝。这也是我面临的一个问题。