我有一个需要将文件上传到S3的播放应用程序。我们正在使用Scala开发并使用Java AWS SDK。
我在尝试上传文件时遇到问题,在使用预先签名的网址时,我一直收到403 SignatureDoesNotMatch。使用AWS Java SDK通过以下代码生成URL:
def generatePresignedPutRequest(filename: String) = {
val expiration = new java.util.Date();
var msec = expiration.getTime() + 1000 * 60 * 60; // Add 1 hour.
expiration.setTime(msec);
s3 match {
case Some(s3) => s3.generatePresignedUrl(bucketname, filename, expiration, HttpMethod.PUT).toString
case None => {
Logger.warn("S3 is not availiable. Cannot generate PUT request.")
"URL not availiable"
}
}
}
对于我们遵循的前端代码ioncannon article。
上传文件的js函数(与文章中使用的相同)
function uploadToS3(file, url)
{
var xhr = createCORSRequest('PUT', url);
if (!xhr)
{
setProgress(0, 'CORS not supported');
}
else
{
xhr.onload = function()
{
if(xhr.status == 200)
{
setProgress(100, 'Upload completed.');
}
else
{
setProgress(0, 'Upload error: ' + xhr.status);
}
};
xhr.onerror = function()
{
setProgress(0, 'XHR error.');
};
xhr.upload.onprogress = function(e)
{
if (e.lengthComputable)
{
var percentLoaded = Math.round((e.loaded / e.total) * 100);
setProgress(percentLoaded, percentLoaded == 100 ? 'Finalizing.' : 'Uploading.');
}
};
xhr.setRequestHeader('Content-Type', 'image/png');
xhr.setRequestHeader('x-amz-acl', 'authenticated-read');
xhr.send(file);
}
}
服务器的响应是
<?xml version="1.0" encoding="UTF-8"?>
<Error><Code>SignatureDoesNotMatch</Code>
<Message>The request signature we calculated does not match the signature you provided. Check your key and signing method.</Message>
<StringToSignBytes>50 55 bla bla bla...</StringToSignBytes>
<RequestId>F7A8F1659DE5909C</RequestId>
<HostId>q+r+2T5K6mWHLKTZw0R9/jm22LyIfZFBTY8GEDznfmJwRxvaVJwPiu/hzUfuJWbW</HostId>
<StringToSign>PUT
image/png
1387565829
x-amz-acl:authenticated-read
/mybucketname/icons/f5430c16-32da-4315-837f-39a6cf9f47a1</StringToSign>
<AWSAccessKeyId>myaccesskey</AWSAccessKeyId></Error>
我已经配置了CORS,双重检查了aws凭据并尝试更改请求标头。我总是得到相同的结果。 亚马逊为什么告诉我签名不匹配?
答案 0 :(得分:25)
怀疑OP仍然存在问题,但对于其他遇到此问题的人来说,答案是:
在向S3发出签名请求时,AWS会检查以确保签名与浏览器发送的HTTP标头信息完全匹配。不幸的是,这需要阅读:http://s3.amazonaws.com/doc/s3-developer-guide/RESTAuthentication.html
但是在上面的代码中实际情况并非如此,Javascript正在发送:
xhr.setRequestHeader('Content-Type', 'image/png');
xhr.setRequestHeader('x-amz-acl', 'authenticated-read');
但是在Java / Scala中,调用s3.generatePresignedUrl而不传入其中任何一个。因此,生成的签名实际上是告诉S3拒绝任何带有Content-Type或x-ams-acl标头集的内容。哎呀(我也因此而堕落)。
我看过浏览器会自动发送内容类型,所以即使它们没有明确添加到标题中,它们仍然会进入S3。所以问题是,我们如何在签名中添加Content-Type和x-amz-acl标头?
AWS SDK中有几个重载的generatePresignedUrl函数,但只有其中一个函数允许我们传递bucket-name,filename,expiration-date和http-method之外的任何其他函数。
解决方案是:
以下是要使用的GeneratePresignedUrlRequest的正确函数定义:
AWS GitHub repo上的函数代码也有助于我了解如何编写解决方案代码。希望这会有所帮助。
答案 1 :(得分:9)
我刚使用NodeJs AWS SDK遇到此问题。 这是因为使用了有效的凭据,但没有足够的权限。 更改为我的管理员密钥修复此问题而不更改代码!
答案 2 :(得分:4)
我遇到了类似的问题,设置配置signatureVersion: 'v4'
帮我解决了问题 -
在JavaScript中:
var s3 = new AWS.S3({
signatureVersion: 'v4'
});
改编自https://github.com/aws/aws-sdk-js/issues/902#issuecomment-184872976
答案 3 :(得分:1)
我有同样的问题,但删除内容类型工作正常。特此分享完整的代码。
public class GeneratePresignedUrlAndUploadObject {
private static final String BUCKET_NAME = "<YOUR_AWS_BUCKET_NAME>";
private static final String OBJECT_KEY = "<YOUR_AWS_KEY>";
private static final String AWS_ACCESS_KEY = "<YOUR_AWS_ACCESS_KEY>";
private static final String AWS_SECRET_KEY = "<YOUR_AWS_SECRET_KEY>";
public static void main(String[] args) throws IOException {
BasicAWSCredentials awsCreds = new BasicAWSCredentials(AWS_ACCESS_KEY, AWS_SECRET_KEY);
AmazonS3 s3Client = AmazonS3ClientBuilder.standard().withRegion(Regions.US_EAST_1)
.withCredentials(new AWSStaticCredentialsProvider(awsCreds)).build();
try {
System.out.println("Generating pre-signed URL.");
java.util.Date expiration = new java.util.Date();
long milliSeconds = expiration.getTime();
milliSeconds += 1000 * 60 * 60;
expiration.setTime(milliSeconds);
GeneratePresignedUrlRequest generatePresignedUrlRequest =
new GeneratePresignedUrlRequest(BUCKET_NAME, OBJECT_KEY);
generatePresignedUrlRequest.setMethod(HttpMethod.PUT);
generatePresignedUrlRequest.setExpiration(expiration);
URL url = s3Client.generatePresignedUrl(generatePresignedUrlRequest);
UploadObject(url);
System.out.println("Pre-Signed URL = " + url.toString());
} catch (AmazonServiceException exception) {
System.out.println("Caught an AmazonServiceException, " +
"which means your request made it " +
"to Amazon S3, but was rejected with an error response " +
"for some reason.");
System.out.println("Error Message: " + exception.getMessage());
System.out.println("HTTP Code: " + exception.getStatusCode());
System.out.println("AWS Error Code:" + exception.getErrorCode());
System.out.println("Error Type: " + exception.getErrorType());
System.out.println("Request ID: " + exception.getRequestId());
} catch (AmazonClientException ace) {
System.out.println("Caught an AmazonClientException, " +
"which means the client encountered " +
"an internal error while trying to communicate" +
" with S3, " +
"such as not being able to access the network.");
System.out.println("Error Message: " + ace.getMessage());
}
}
public static void UploadObject(URL url) throws IOException
{
HttpURLConnection connection=(HttpURLConnection) url.openConnection();
connection.setDoOutput(true);
connection.setRequestMethod("PUT");
OutputStreamWriter out = new OutputStreamWriter(
connection.getOutputStream());
out.write("This text uploaded as object.");
out.close();
int responseCode = connection.getResponseCode();
System.out.println("Service returned response code " + responseCode);
}
}
答案 4 :(得分:0)
遇到问题,Windows上的mime类型将fileType设置为空字符串,但它不起作用。只需处理空字符串并添加一些文件类型。
答案 5 :(得分:0)
我使用SignatureDoesNotMatch
遇到Java AWS SDK
错误。就我而言,SignatureDoesNotMatch
错误是在升级maven依赖项之后发生的,而我的代码没有更改(因此凭据正确且未被更改)。将依赖项org.apache.httpcomponents:httpclient
从版本4.5.6
升级到4.5.7
(实际上是将Spring Boot
从2.1.2
升级到2.1.3
之后,{{1 }}已指定bom
版本),在执行某些httpclient
之类的AWS开发工具包S3请求时,代码变成了抛出异常。
深入研究根本原因后,我发现AmazonS3.getObject
库确实使用规范化URI破坏了更改,从而影响了Java AWS SDK S3。请查看打开的GitHub票证org.apache.httpcomponents:httpclient:4.5.7 breaks fetching S3 objects,了解更多详细信息。
答案 6 :(得分:0)
对我来说同样的问题,但原因不同。我使用的是POST而不是PUT
答案 7 :(得分:0)
如果您的访问密钥和秘密密钥很好,但是却说“ SignatureDoesNotMAtch”,请检查您的秘密密钥,它可能具有某些特殊字符,例如+ /-/ *
转到aws并生成另一个访问密钥,其中秘密密钥没有这些访问密钥。然后重试:)