S3 PutObject预签名URL签名不匹配问题(GetObject正常)
S3 PutObject预签名URL签名不匹配问题
我在生成Amazon S3的PutObject操作预签名URL时,使用该URL执行PUT请求会触发SignatureDoesNotMatch错误,但GetObject的预签名URL生成和使用都正常,说明凭证是有效的。我已经关闭了所有公共访问阻止设置,配置了允许PutObject的Bucket策略和合适的CORS规则,试过用s3库和s3manager生成URL,用curl执行PUT请求还是报错。
Bucket策略
{ "Version": "2012-10-17", "Statement": [ { "Sid": "AllowPutObject", "Effect": "Allow", "Principal": "*", "Action": "s3:PutObject", "Resource": "arn:aws:s3:::adobe-sign-test/*" } ] }
Bucket CORS配置
[ { "AllowedHeaders": [ "*" ], "AllowedMethods": [ "GET", "HEAD", "PUT" ], "AllowedOrigins": [ "*" ], "ExposeHeaders": [] } ]
预签名URL生成代码
func getPresignedURL(bucket, key, operation string, expiresIn int64, region string, s3AccessKeyID string, s3SecretAccessKey string) string { sess := session.Must(session.NewSession(&aws.Config{ Region: aws.String(region), Credentials: credentials.NewStaticCredentials( s3AccessKeyID, s3SecretAccessKey, "", ), })) uploader := s3manager.NewUploader(sess) if operation == "PutObject" { // Generate a pre-signed URL for a PutObject operation uploadUrl, _ := uploader.Upload(&s3manager.UploadInput{ Bucket: aws.String(bucket), Key: aws.String(key), ACL: aws.String("bucket-owner-full-control"), }) return uploadUrl.Location } // Generate a pre-signed URL for a GetObject operation req, _ := uploader.S3.GetObjectRequest(&s3.GetObjectInput{ Bucket: aws.String(bucket), Key: aws.String(key), }) getUrl, err := req.Presign(time.Duration(expiresIn) * time.Second) if err != nil { fmt.Println(err) return "" } return getUrl }
执行PUT请求的curl命令
curl -X PUT -T some-file.jpg "https://adobe-sign-test.s3.eu-north-1.amazonaws.com/hejsan.jpg?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAQJGLAAKTL3QQFZ73%2F20230706%2Feu-north-1%2Fs3%2Faws4_request&X-Amz-Date=20230706T074846Z&X-Amz-Expires=3600&X-Amz-SignedHeaders=host&X-Amz-Signature=5eb429ee8efc1c8ffcae64d77a588119cfde81512bc5c4516a1120b20e26cac7"
错误信息
<?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><AWSAccessKeyId>AKIAQJGLAAKTL3QQFZ73</AWSAccessKeyId><StringToSign>AWS4-HMAC-SHA256 20230706T074846Z 20230706/eu-north-1/s3/aws4_request 7666f6675cc3fe3a3aa20f98928aeccd4b9bc851666fcf03fb425d5819d7e72d</StringToSign><SignatureProvided>5eb429ee8efc1c8ffcae64d77a588119cfde81512bc5c4516a1120b20e26cac7</SignatureProvided><StringToSignBytes>41 57 53 34 2d 48 4d 41 43 2d 53 48 41 32 35 36 0a 32 30 32 33 30 37 30 36 54 30 37 34 38 34 36 5a 0a 32 30 32 33 30 37 30 36 2f 65 75 2d 6e 6f 72 74 68 2d 31 2f 73 33 2f 61 77 73 34 5f 72 65 71 75 65 73 74 0a 37 36 36 36 66 36 36 37 35 63 63 33 66 65 33 61 33 61 61 32 30 66 39 38 39 32 38 61 65 63 63 64 34 62 39 62 63 38 35 31 36 36 36 66 63 66 30 33 66 62 34 32 35 64 35 38 31 39 64 37 65 37 32 64</StringToSignBytes><CanonicalRequest>PUT /hejsan.jpg X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAQJGLAAKTL3QQFZ73%2F20230706%2Feu-north-1%2Fs3%2Faws4_request&X-Amz-Date=20230706T074846Z&X-Amz-Expires=3600&X-Amz-SignedHeaders=host host:adobe-sign-test.s3.eu-north-1.amazonaws.com host UNSIGNED-PAYLOAD</CanonicalRequest><CanonicalRequestBytes>50 55 54 0a 2f 68 65 6a 73 61 6e 2e 6a 70 67 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 4a 47 4c 41 41 4b 54 4c 33 51 51 46 5a 37 33 25 32 46 32 30 32 33 30 37 30 36 25 32 46 65 75 2d 6e 6f 72 74 68 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 33 30 37 30 36 54 30 37 34 38 34 36 5a 26 58 2d 41 6d 7a 2d 45 78 70 69 72 65 73 3d 33 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 0a 68 6f 73 74 3a 61 64 6f 62 65 2d 73 69 67 6e 2d 74 65 73 74 2e 73 33 2e 65 75 2d 6e 6f 72 74 68 2d 31 2e 61 6d 61 7a 6f 6e 61 77 73 2e 63 6f 6d 0a 0a 68 6f 73 74 0a 55 4e 53 49 47 4e 45 44 2d 50 41 59 4c 4f 41 44</CanonicalRequestBytes><RequestId>SMFDTR996NQE9DDV</RequestId><HostId>VehdLPGdnoVZclkNKs2+lTjFpAssA1Xe+HZSj5ZCCVK2QnumQHqzsePFg3TWWaai3+vqGgnwxpjMd3b8526I7Q==</HostId></Error>
问题原因及解决方法
核心问题:错误使用s3manager.Upload生成预签名URL
你当前用s3manager.Upload来生成PutObject的预签名URL是错误的——s3manager.Upload本身是直接执行上传操作,而不是生成预签名URL。它返回的uploadUrl.Location是上传成功后的对象访问地址,不是预签名的PUT URL,所以用这个地址执行PUT请求必然会签名不匹配。
正确的PutObject预签名URL生成方式
应该和GetObject一样,通过S3客户端的PutObjectRequest来生成预签名URL,修改代码如下:
func getPresignedURL(bucket, key, operation string, expiresIn int64, region string, s3AccessKeyID string, s3SecretAccessKey string) string { sess := session.Must(session.NewSession(&aws.Config{ Region: aws.String(region), Credentials: credentials.NewStaticCredentials( s3AccessKeyID, s3SecretAccessKey, "", ), })) s3Client := s3.New(sess) if operation == "PutObject" { // 正确生成PutObject的预签名URL req, err := s3Client.PutObjectRequest(&s3.PutObjectInput{ Bucket: aws.String(bucket), Key: aws.String(key), ACL: aws.String("bucket-owner-full-control"), }) if err != nil { fmt.Println(err) return "" } putUrl, err := req.Presign(time.Duration(expiresIn) * time.Second) if err != nil { fmt.Println(err) return "" } return putUrl } // GetObject的预签名URL生成逻辑保持不变 req, err := s3Client.GetObjectRequest(&s3.GetObjectInput{ Bucket: aws.String(bucket), Key: aws.String(key), }) if err != nil { fmt.Println(err) return "" } getUrl, err := req.Presign(time.Duration(expiresIn) * time.Second) if err != nil { fmt.Println(err) return "" } return getUrl }
其他注意事项
- 确保生成预签名URL时指定的
ACL(这里是bucket-owner-full-control)和后续PUT请求中没有冲突——如果请求中额外添加了ACL头,需要把x-amz-acl加入签名头列表。 - 用curl测试时,不要修改预签名URL中的任何参数,包括签名、日期等,确保请求头和签名时的
SignedHeaders一致。
内容的提问来源于stack exchange,提问作者Jimmie Johansson
相关产品推荐
相关产品推荐

