使用Ionic 2通过预签名URL上传到S3时出现SignatureDoesNotMatch错误

17

我正在尝试将视频上传到s3,并拥有预先签名的PUT URL。以下是执行此操作的代码。

import {Component} from '@angular/core';
import {NavController} from 'ionic-angular';
import {MediaCapture} from 'ionic-native';
import {Http} from '@angular/http';
import { Transfer } from 'ionic-native';

@Component({
  selector: 'page-home',
  templateUrl: 'home.html'
})

export class HomePage {

    public base64Image: string;

    constructor(private navController: NavController, public http: Http) {
        this.base64Image = "https://placehold.it/150x150";
    }

    public takeVideo() {
        MediaCapture.captureVideo({limit:2}).then(function(videoData){
            var link = "https://mysamplebucket.s3.amazonaws.com/non-tp/esx.mov?AWSAccessKeyId=TEMP_KEYY&Expires=1482290587&Signature=JUIHHI%2FcnLkqSVg%3D&x-amz-security-token=FQoDYXDGRfTXk6hma0Rxew6yraAX%2FlYGaQmYLwkvsuuB3%2F%2FtPvGDVs3dIQG0Ty3MeMjn0p%%26djt5xhAMk73pndJbZP0tCYYlvPvlUAyL8x7O%%2B3AwEa%%2B9b43yarIuPLCvujmKLTDyi%%3D%3Di";

            var options: any;

            options = {
             fileKey: 'file',
             fileName: 'esx.mov',
             httpMethod: 'PUT',
             chunkedMode: false,
             mimeType: 'video/quicktime',
             encodeURI: false,
             headers: {
                'Content-Type': 'video/quicktime'
              }
            };

            var ft = new Transfer();
            ft.upload(videoData[0].fullPath, link, options, false)
                .then((result: any) => {
                    this.success(result);
                }).catch((error: any) => {
                    this.failed(error);
                }); 

        }, function(err){
            alert(err);
        });
    }


}

这里是生成预签名PUT URL的代码。

var params = {Bucket: s3_bucket, Key: filename, Expires: 900000};
var url = { 
    'url' : s3.getSignedUrl('putObject', params)
};

我遇到了SignatureDoesNotMatch错误。错误提示是:“我们计算的请求签名与您提供的签名不匹配。请检查您的密钥和签名方法。”我不确定我在这里做错了什么 —— 我查看了一些其他的SO和Ionic问题,并尝试了他们建议的方法,但都没有效果。你有什么关于我做错了什么的想法吗?


1
你说你“有”一个预签名的PUT URL,但你似乎没有展示用于生成它的代码。这几乎肯定是问题所在。请展示这段代码,特别是用于生成规范请求的参数?(当然不包括秘密密钥)。请注意,内容类型是必须包含的参数之一。 - Michael - sqlbot
@Michael-sqlbot 我有生成它的服务器端代码。有一个API端点,我调用它来获取它。现在由于我正在测试这个代码,我使用Postman调用API端点,并将其放入link变量中。 - blehadfas1
1
好的,但那似乎是问题的根源——假设签名代码正常工作,那么问题似乎出在您传递给它以获取链接的参数上。我们需要更多信息才能诊断这个问题。 - Michael - sqlbot
3个回答

26

您的上传PUT请求将有一个Content-Type: video/quicktime头部。

当请求中(而不是响应中)存在Content-Type头时,它的值是Signature V2规范请求中一个非可选的组件...这意味着您必须将其传递给生成签名的代码。

var params = {Bucket: s3_bucket, Key: filename, Expires: 900000};也需要将此字符串(video/quicktime, 在本例中)作为ContentType: ...传递给它,用于PUT请求(但对于GET请求则不需要,因为它描述的是您正在发送的内容,而GET请求通常不会发送任何实际内容)。

SDK文档似乎没有具体提到这一点,但在S3中绝对需要。


你说的有道理,但是这里让我感到困惑。我有一个用Swift编写的应用程序版本,在其中使用Almaofire进行PUT请求,而不指定Content-Type,向上面代码中获取的URL发送请求,它可以正常工作。另外,由于在生成URL时我没有指定Content-Type,为什么从PUT请求中删除Content-Type就不能正常工作呢? - blehadfas1
1
将“ContentType: video/quicktime”添加到生成PUT URL的代码中解决了问题! - blehadfas1
1
为什么从PUT请求中删除Content-Type无法生效?因为不指定它可能无法将其排除在用户代理添加的其他值之外。mimeType:'video/quicktime' 可能具有设置Content-Type的等效效果,因为两者大致是同义词,或者用户代理可能默认为一般类型,例如application/octet-streamapplication/binary。您必须观察实际的HTTP事务才能确定。 - Michael - sqlbot
非常感谢!我添加了ContentType,现在一切都正常。 - 鄭元傑

4
假如有其他人看到这篇文章并且情况跟我类似,当我的S3存储桶的CORS配置没有包含<AllowedHeader>*</AllowedHeader>时,我也遇到了类似的SignatureDoesNotMatchError错误。
我在将一个存储桶迁移到另一个存储桶时遇到了这个问题,因为我复制了所有设置,除了CORS配置。

1
我正在使用 aws go sdk 生成 预签名的请求URL,因此需要在存储桶CORS设置中特别添加 <AllowedHeader>Content-Type</ AllowedHeader> - ShahNewazKhan

1
当我们下载已上传的文件时,遇到了这个问题。我们收到了预签名URL,但是当尝试使用该预签名URL下载文件时,它显示“签名不匹配”。 我们向AWS报告了一个工单,因为所有方法都失败了,我们得到了解决方案。情况是我们启用了自定义AWS KMS加密的S3存储桶,但是在使用GeneratePresignedUrlRequest API时,我们尝试发送“kms key”。AWS说,我们不必发送KMS密钥,而是从客户端发送未加密的内容。当我说未加密时,它并不完全是这样,它已经以加密形式出现,当我们使用“AWSS3V4SignerType”进行签名时,我们发送了kms id作为额外参数,这是不必要的。希望这讲得通。 AWS在标头中查找的参数包括:
  1. 算法
  2. 凭证范围
  3. 已签名标头
  4. 日期
  5. 过期日期
  6. 签名
  7. KMS密钥-我们传递了这个,但是实际上不需要。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接