即使输入了错误的访问密钥或秘密密钥,也要从s3获取preSignedUrl

问题描述

我正在生成一个preSignedUrl,然后通过该URL上传文件。 问题是,即使我输入了错误的访问密钥或秘密密钥,我也会得到preSignedUrl,尽管如果我尝试使用该URL上传,也会出现400错误

  <?xml version="1.0" encoding="UTF-8"?>
    <Error>
        <Code>AuthorizationQueryParametersError</Code>
        <Message>Query-string authentication version 4 requires the X-Amz-Algorithm,X-Amz-Credential,X-Amz-Signature,X-Amz-Date,X-Amz-SignedHeaders,and X-Amz-Expires parameters.</Message>
        <RequestId>{requestId}</RequestId>
        <HostId>{hostId}</HostId>
    </Error>

生成preSignedUrl时是否有某种方式可以得到此错误,所以我不必尝试上传文件

    AmazonS3 s3Client = AmazonS3ClientBuilder.standard()
                    .withCredentials(new AWsstaticCredentialsProvider(new BasicAWSCredentials("accessKey","secretKey")))
                    .withRegion(clientRegion)
                    .build();
    
    GeneratePresignedUrlRequest generatePresignedUrlRequest = new GeneratePresignedUrlRequest(bucketName,objectKey)
     .withMethod(HttpMethod.PUT)
     .withExpiration(expiration);

    URL url = s3Client.generatePresignedUrl(generatePresignedUrlRequest);

解决方法

生成预签名URL不需要API调用;它可以由框架使用指定的访问密钥和机密生成。

生成的URL将在收到请求后由S3验证,并且显然只有在使用有效凭据生成请求时才会被接受。

底线:为了验证您的凭证,您需要发出一个实际上执行对AWS的调用的API请求。几乎可以是s3Client上的任何其他方法。

,

让我们从这里开始:

.withCredentials(new AWSStaticCredentialsProvider(new BasicAWSCredentials("accessKey","secretKey")))

静态凭据违反AWS best practice。而是依靠通过环境变量或执行角色(在EC2,ECS或Lambda上运行时)提供的凭据。

验证凭据有效的唯一方法是尝试使用它们。您可以编写一个小的虚拟文件,但是由于最终在S3上的一致性,这可能会导致本应读取该文件的任何问题。

还有一个问题是,您提供的URL到期时间可能与凭证的生存期不符。

所有这些问题的最佳解决方案是创建一个角色,该角色可以访问S3上的文件,并且持续时间与您的URL到期时间一致(请注意,最长为12小时),然后明确承担该角色为了构造请求:

final String assumedRoleArn = "arn:aws:iam::123456789012:role/Example";
final String sessionName = "example";
final String bucketName = "com-example-mybucket";
final String objectKey = "myfile.txt";

final int expirationSeconds = 12 * 3600;
final Date expiresAt = new Date(System.currentTimeMillis() + expirationSeconds * 1000);

AWSSecurityTokenService stsClient = AWSSecurityTokenServiceClientBuilder.defaultClient();

AWSCredentialsProvider credentialsProvider = new STSAssumeRoleSessionCredentialsProvider.Builder(assumedRoleArn,sessionName)
                                             .withStsClient(stsClient)
                                             .withRoleSessionDurationSeconds(expirationSeconds)
                                             .build();

AmazonS3 s3Client = AmazonS3ClientBuilder.standard().withCredentials(credentialsProvider).build();

URL presignedUrl = s3Client.generatePresignedUrl(bucketName,objectKey,expiresAt,HttpMethod.PUT);