s3 预签名 url:带有元数据的 URL 被拒绝

问题描述 投票:0回答:1

我遇到与附加自定义元数据信息的 PUT 预签名 url 相关的问题。

我的相关代码是:

public URL generatePushResourceLocation(DocumentId documentId, String contentMD5) {
    String key = documentId.getId().toString();
    PutObjectRequest.Builder putObjectRequestBuilder = PutObjectRequest.builder().bucket(bucket).key(key)
            .metadata(Map.of("mk1", "mv1"));

    PutObjectRequest putObjectRequest = putObjectRequestBuilder.build();
    PutObjectPresignRequest request = PutObjectPresignRequest.builder().putObjectRequest(putObjectRequest)
            .signatureDuration(Duration.ofMinutes(1)).build();

    PresignedPutObjectRequest presignPutObject = s3Presigner.presignPutObject(request);
    URL signedPutURL = presignPutObject.url();

    log.info("Push Presigned URL created: [{}]", signedPutURL.toString());

    return signedPutURL;
}

如您所见,我只是生成一个带有元数据的 put 预签名 url。

当我尝试使用这个预先签名的网址时,我得到:

我们计算的请求签名与您提供的签名不匹配。检查您的密钥和签名方法。

完整回复是:

<Error>
    <Code>SignatureDoesNotMatch</Code>
    <Message>The request signature we calculated does not match the signature you provided. Check your key and signing method.</Message>
    <AWSAccessKeyId>AKIAQ3EGWRZYSIU7ENEO</AWSAccessKeyId>
    <StringToSign>AWS4-HMAC-SHA256
20240402T122245Z
20240402/eu-west-1/s3/aws4_request
66cdf5260d90912bebfd2cc337f5c1bcfb8fb6b4847852bd427627f82bf9b8c8</StringToSign>
    <SignatureProvided>bd7e26bf661eea50966733816872544a6bf75151e30f819f2239725fcae08203</SignatureProvided>
    <StringToSignBytes>41 57 53 34 2d 48 4d 41 43 2d 53 48 41 32 35 36 0a 32 30 32 34 30 34 30 32 54 31 32 32 32 34 35 5a 0a 32 30 32 34 30 34 30 32 2f 65 75 2d 77 65 73 74 2d 31 2f 73 33 2f 61 77 73 34 5f 72 65 71 75 65 73 74 0a 36 36 63 64 66 35 32 36 30 64 39 30 39 31 32 62 65 62 66 64 32 63 63 33 33 37 66 35 63 31 62 63 66 62 38 66 62 36 62 34 38 34 37 38 35 32 62 64 34 32 37 36 32 37 66 38 32 62 66 39 62 38 63 38</StringToSignBytes>
    <CanonicalRequest>PUT
/espaidoc-des/1117beac-c2b4-414a-a518-1c4ac805644d
X-Amz-Algorithm=AWS4-HMAC-SHA256&amp;X-Amz-Credential=AKIAQ3EGWRZYSIU7ENEO%2F20240402%2Feu-west-1%2Fs3%2Faws4_request&amp;X-Amz-Date=20240402T122245Z&amp;X-Amz-Expires=600&amp;X-Amz-SignedHeaders=host%3Bx-amz-meta-mk1
host:s3.eu-west-1.amazonaws.com
x-amz-meta-mk1:

host;x-amz-meta-mk1
UNSIGNED-PAYLOAD</CanonicalRequest>
    <CanonicalRequestBytes>50 55 54 0a 2f 65 73 70 61 69 64 6f 63 2d 64 65 73 2f 31 31 31 37 62 65 61 63 2d 63 32 62 34 2d 34 31 34 61 2d 61 35 31 38 2d 31 63 34 61 63 38 30 35 36 34 34 64 0a 58 2d 41 6d 7a 2d 41 6c 67 6f 72 69 74 68 6d 3d 41 57 53 34 2d 48 4d 41 43 2d 53 48 41 32 35 36 26 58 2d 41 6d 7a 2d 43 72 65 64 65 6e 74 69 61 6c 3d 41 4b 49 41 51 33 45 47 57 52 5a 59 53 49 55 37 45 4e 45 4f 25 32 46 32 30 32 34 30 34 30 32 25 32 46 65 75 2d 77 65 73 74 2d 31 25 32 46 73 33 25 32 46 61 77 73 34 5f 72 65 71 75 65 73 74 26 58 2d 41 6d 7a 2d 44 61 74 65 3d 32 30 32 34 30 34 30 32 54 31 32 32 32 34 35 5a 26 58 2d 41 6d 7a 2d 45 78 70 69 72 65 73 3d 36 30 30 26 58 2d 41 6d 7a 2d 53 69 67 6e 65 64 48 65 61 64 65 72 73 3d 68 6f 73 74 25 33 42 78 2d 61 6d 7a 2d 6d 65 74 61 2d 6d 6b 31 0a 68 6f 73 74 3a 73 33 2e 65 75 2d 77 65 73 74 2d 31 2e 61 6d 61 7a 6f 6e 61 77 73 2e 63 6f 6d 0a 78 2d 61 6d 7a 2d 6d 65 74 61 2d 6d 6b 31 3a 0a 0a 68 6f 73 74 3b 78 2d 61 6d 7a 2d 6d 65 74 61 2d 6d 6b 31 0a 55 4e 53 49 47 4e 45 44 2d 50 41 59 4c 4f 41 44</CanonicalRequestBytes>
    <RequestId>6Q3P82ZWPWNAC5V0</RequestId>
    <HostId>uEVaiZU1gKj0n6KNIrkuWHELwJD9El/DdQ1uKu+o5dwsEvoFdaVTv/gL0e6AfbN/kthrJlo0+9A=</HostId>
</Error>

我的curl命令:

curl --request PUT --header '内容类型:' --data-binary '@hurl/push-document/lorem.txt' 'https://s3.eu-west-1.amazonaws.com/espaidoc -des/54c07cd8-1bce-43b9-ab32-28a7e9108c19?X-Amz-算法=AWS4-HMAC-SHA256&X-Amz-Date=20240402T123532Z&X-Amz-SignedHeaders=主机%3Bx-amz-meta-mk1&X-Amz-Expires=60&X -Amz-Credential=AKIAQ3EGWRZYSIU7ENEO%2F20240402%2Feu-west-1%2Fs3%2Faws4_request&X-Amz-Signature=ebfbbcfbbaadae7b580ed0a6cddb503371a6aa11b59e229fcc5663225f45a9be'

有什么想法吗?

amazon-web-services amazon-s3
1个回答
0
投票

我刚刚运行了以下 AWS S3 代码,它运行良好。我使用的文件是我在 Java 程序中指定的名为 book.pdk 的 PDF。输出是:

2024-04-02 11:29:17 [main] INFO  com.example.s3.util.PresignUrlUtils:20 - Bucket [b-e69cb1f5-e073-4724-b285-787f0951b193] created
2024-04-02 11:29:17 [main] INFO  com.example.s3.GeneratePresignedUrlAndPutFileWithMetadata:105 - Presigned URL to upload a file to: [https://b-xxxxxx5-787f0951b193.s3.amazonaws.com/k-30d2854d-8a89-4c40-9519-d7e14b77e6ad?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20240402T152917Z&X-Amz-SignedHeaders=host%3Bx-amz-meta-author%3Bx-amz-meta-version&X-Amz-Credential=AKIA33JWY3BX2TYWHOOR%2F20240402%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Expires=600&X-Amz-Signature=cb1356a0afe7e565d64e8ac45603d5dfd4e535af285188600485268ebbe2a8ae]
2024-04-02 11:29:17 [main] INFO  com.example.s3.GeneratePresignedUrlAndPutFileWithMetadata:106 - HTTP method: [PUT]
2024-04-02 11:29:21 [main] INFO  com.example.s3.GeneratePresignedUrlAndPutFileWithMetadata:116 - Begin [C:\AWS\book.pdf] upload
2024-04-02 11:29:38 [main] INFO  com.example.s3.GeneratePresignedUrlAndPutFileWithMetadata:142 - HTTP response code is 200
2024-04-02 11:29:39 [main] INFO  com.example.s3.util.PresignUrlUtils:36 - Object [k-30d2854d-8a89-4c40-9519-d7e14b77e6ad] deleted
2024-04-02 11:29:39 [main] INFO  com.example.s3.GeneratePresignedUrlAndPutFileWithMetadata:153 - Begin [C:\AWS\book.pdf] upload
2024-04-02 11:29:58 [main] INFO  com.example.s3.GeneratePresignedUrlAndPutFileWithMetadata:166 - HTTP response code is 200
2024-04-02 11:29:58 [main] INFO  com.example.s3.util.PresignUrlUtils:36 - Object [k-30d2854d-8a89-4c40-9519-d7e14b77e6ad] deleted
2024-04-02 11:29:58 [main] INFO  com.example.s3.GeneratePresignedUrlAndPutFileWithMetadata:177 - Begin [C:\AWS\book.pdf] upload
2024-04-02 11:30:17 [main] INFO  com.example.s3.GeneratePresignedUrlAndPutFileWithMetadata:197 - Response code: 200
2024-04-02 11:30:18 [main] INFO  com.example.s3.util.PresignUrlUtils:36 - Object [k-30d2854d-8a89-4c40-9519-d7e14b77e6ad] deleted
2024-04-02 11:30:18 [main] INFO  com.example.s3.util.PresignUrlUtils:28 - Bucket [b-e69cb1f5-e073-4724-b285-787f0951b193] deleted

Java V2 代码在这里:

import com.example.s3.util.PresignUrlUtils;
import org.slf4j.Logger;
import software.amazon.awssdk.core.internal.sync.FileContentStreamProvider;
import software.amazon.awssdk.http.HttpExecuteRequest;
import software.amazon.awssdk.http.HttpExecuteResponse;
import software.amazon.awssdk.http.SdkHttpClient;
import software.amazon.awssdk.http.SdkHttpMethod;
import software.amazon.awssdk.http.SdkHttpRequest;
import software.amazon.awssdk.http.apache.ApacheHttpClient;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.PutObjectRequest;
import software.amazon.awssdk.services.s3.model.S3Exception;
import software.amazon.awssdk.services.s3.presigner.S3Presigner;
import software.amazon.awssdk.services.s3.presigner.model.PresignedPutObjectRequest;
import software.amazon.awssdk.services.s3.presigner.model.PutObjectPresignRequest;

import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.Duration;
import java.util.Map;
import java.util.UUID;
// snippet-end:[presigned.java2.generatepresignedurlandputfilewithmetadata.import]

/**
 * Before running this Java V2 code example, set up your development environment, including your credentials.
 * <p>
 * For more information, see the following documentation topic:
 * <p>
 * https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/get-started.html
 */

public class GeneratePresignedUrlAndPutFileWithMetadata {
    private static final Logger logger = org.slf4j.LoggerFactory.getLogger(GeneratePresignedUrlAndPutFileWithMetadata.class);
    private final static S3Client s3Client = S3Client.create();

    public static void main(String[] args) {
        String bucketName = "b-" + UUID.randomUUID();
        String keyName = "k-" + UUID.randomUUID();
        // Uncomment the following two lines and comment out the previous two lines to use an image file instead of a PDF file.
        //String resourcePath = "image.png";
        //String contentType = "image/png";

        Map<String, String> metadata = Map.of(
                "author", "Bob",
                "version", "1.0.0.0"
        );

        PresignUrlUtils.createBucket(bucketName, s3Client);
        GeneratePresignedUrlAndPutFileWithMetadata presign = new GeneratePresignedUrlAndPutFileWithMetadata();
        try {
            String presignedUrlString = presign.createPresignedUrl(bucketName, keyName, metadata);

            File myFile = new File("C:\\AWS\\book.pdf");
            presign.useHttpUrlConnectionToPut(presignedUrlString, myFile, metadata);
            PresignUrlUtils.deleteObject(bucketName, keyName, s3Client);
            presign.useHttpClientToPut(presignedUrlString, myFile, metadata);
            PresignUrlUtils.deleteObject(bucketName, keyName, s3Client);
            presign.useSdkHttpClientToPut(presignedUrlString, myFile, metadata);


        } finally {
            PresignUrlUtils.deleteObject(bucketName, keyName, s3Client);
            PresignUrlUtils.deleteBucket(bucketName, s3Client);
        }
    }

    // snippet-start:[presigned.java2.generatepresignedurlandputfilewithmetadata.main]
    // snippet-start:[presigned.java2.generatepresignedurlandputfilewithmetadata.createpresignedurl]
    /* Create a presigned URL to use in a subsequent PUT request */
    public String createPresignedUrl(String bucketName, String keyName, Map<String, String> metadata) {
        try (S3Presigner presigner = S3Presigner.create()) {

            PutObjectRequest objectRequest = PutObjectRequest.builder()
                    .bucket(bucketName)
                    .key(keyName)
                    .metadata(metadata)
                    .build();

            PutObjectPresignRequest presignRequest = PutObjectPresignRequest.builder()
                    .signatureDuration(Duration.ofMinutes(10))  // The URL expires in 10 minutes.
                    .putObjectRequest(objectRequest)
                    .build();


            PresignedPutObjectRequest presignedRequest = presigner.presignPutObject(presignRequest);
            String myURL = presignedRequest.url().toString();
            logger.info("Presigned URL to upload a file to: [{}]", myURL);
            logger.info("HTTP method: [{}]", presignedRequest.httpRequest().method());

            return presignedRequest.url().toExternalForm();
        }
    }
    // snippet-end:[presigned.java2.generatepresignedurlandputfilewithmetadata.createpresignedurl]

    // snippet-start:[presigned.java2.generatepresignedurlandputfilewithmetadata.basichttpclient]
    /* Use the JDK HttpURLConnection (since v1.1) class to do the upload. */
    public void useHttpUrlConnectionToPut(String presignedUrlString, File fileToPut, Map<String, String> metadata) {
        logger.info("Begin [{}] upload", fileToPut.toString());
        try {
            URL presignedUrl = new URL(presignedUrlString);
            HttpURLConnection connection = (HttpURLConnection) presignedUrl.openConnection();
            connection.setDoOutput(true);
            metadata.forEach((k, v) -> connection.setRequestProperty("x-amz-meta-" + k, v));
            connection.setRequestMethod("PUT");
            OutputStream out = connection.getOutputStream();

            try (RandomAccessFile file = new RandomAccessFile(fileToPut, "r");
                 FileChannel inChannel = file.getChannel()) {
                ByteBuffer buffer = ByteBuffer.allocate(8192); //Buffer size is 8k

                while (inChannel.read(buffer) > 0) {
                    buffer.flip();
                    for (int i = 0; i < buffer.limit(); i++) {
                        out.write(buffer.get());
                    }
                    buffer.clear();
                }
            } catch (IOException e) {
                logger.error(e.getMessage(), e);
            }

            out.close();
            connection.getResponseCode();
            logger.info("HTTP response code is " + connection.getResponseCode());

        } catch (S3Exception | IOException e) {
            logger.error(e.getMessage(), e);
        }
    }
    // snippet-end:[presigned.java2.generatepresignedurlandputfilewithmetadata.basichttpclient]

    // snippet-start:[presigned.java2.generatepresignedurlandputfilewithmetadata.jdkhttpclient]
    /* Use the JDK HttpClient (since v11) class to do the upload. */
    public void useHttpClientToPut(String presignedUrlString, File fileToPut, Map<String, String> metadata) {
        logger.info("Begin [{}] upload", fileToPut.toString());

        HttpRequest.Builder requestBuilder = HttpRequest.newBuilder();
        metadata.forEach((k, v) -> requestBuilder.header("x-amz-meta-" + k, v));

        HttpClient httpClient = HttpClient.newHttpClient();
        try {
            final HttpResponse<Void> response = httpClient.send(requestBuilder
                            .uri(new URL(presignedUrlString).toURI())
                            .PUT(HttpRequest.BodyPublishers.ofFile(Path.of(fileToPut.toURI())))
                            .build(),
                    HttpResponse.BodyHandlers.discarding());

            logger.info("HTTP response code is " + response.statusCode());

        } catch (URISyntaxException | InterruptedException | IOException e) {
            logger.error(e.getMessage(), e);
        }
    }
    // snippet-end:[presigned.java2.generatepresignedurlandputfilewithmetadata.jdkhttpclient]

    // snippet-start:[presigned.java2.generatepresignedurlandputfilewithmetadata.sdkhttpclient]
    /* Use the AWS SDK for Java V2 SdkHttpClient class to do the upload. */
    public void useSdkHttpClientToPut(String presignedUrlString, File fileToPut, Map<String, String> metadata) {
        logger.info("Begin [{}] upload", fileToPut.toString());

        try {
            URL presignedUrl = new URL(presignedUrlString);

            SdkHttpRequest.Builder requestBuilder = SdkHttpRequest.builder()
                    .method(SdkHttpMethod.PUT)
                    .uri(presignedUrl.toURI());
            // Add headers
            metadata.forEach((k, v) -> requestBuilder.putHeader("x-amz-meta-" + k, v));
            // Finish building the request.
            SdkHttpRequest request = requestBuilder.build();

            HttpExecuteRequest executeRequest = HttpExecuteRequest.builder()
                    .request(request)
                    .contentStreamProvider(new FileContentStreamProvider(fileToPut.toPath()))
                    .build();

            try (SdkHttpClient sdkHttpClient = ApacheHttpClient.create()) {
                HttpExecuteResponse response = sdkHttpClient.prepareRequest(executeRequest).call();
                logger.info("Response code: {}", response.httpResponse().statusCode());
            }
        } catch (URISyntaxException | IOException e) {
            logger.error(e.getMessage(), e);
        }
    }
    // snippet-end:[presigned.java2.generatepresignedurlandputfilewithmetadata.sdkhttpclient]
    // snippet-end:[presigned.java2.generatepresignedurlandputfilewithmetadata.main]
}
© www.soinside.com 2019 - 2024. All rights reserved.