S3预签名URL出现SignatureDoesNotMatch错误求助
AWS S3预签名URL POST请求签名不匹配问题
我的AWS S3存储桶内有一个名为items/的文件夹,用于存放所有图片。生成预签名URL时已在key中正确指定该路径,但向该URL发送POST请求时出现错误:
"The request signature we calculated does not match the signature you provided. Check your key and signing method."
相关代码实现如下:
@Configuration public class S3Config { @Value("${aws.region}") private String awsRegion; @Bean public S3Client s3Client() { S3Client client = S3Client.builder() .region(Region.of(awsRegion)) .build(); return client; } @Bean public S3Presigner s3Presigner() { return S3Presigner.builder() .region(Region.of(awsRegion)) .credentialsProvider(DefaultCredentialsProvider.create()) .build(); } }
@RestController @RequestMapping("/s3") public class S3Controller { private final S3Service s3Service; @Autowired public S3Controller(S3Service s3Service) { this.s3Service = s3Service; } // Endpoint to generate a presigned URL for PUT operations @GetMapping("/presign-put/{filename}") public ResponseEntity<String> getPresignedPutUrl(@PathVariable String filename) { try { String presignedUrl = s3Service.generatePresignedUrl(filename); return ResponseEntity.ok(presignedUrl); } catch (Exception e) { return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("Failed to generate presigned URL: " + e.getMessage()); } } }
@Service public class S3Service { private final S3Client s3; private final S3Presigner presigner; @Value("${aws.s3.bucketName}") //defined inside of application properties private String bucketName; public S3Service(S3Client s3, S3Presigner s3Presigner) { this.s3 = s3; this.presigner = s3Presigner; } public String generatePresignedUrl(String filename) { String key = "items/" + filename; PutObjectRequest putObjectRequest = PutObjectRequest.builder() .bucket(bucketName) .key(key) .build(); PutObjectPresignRequest presignRequest = PutObjectPresignRequest.builder() .putObjectRequest(putObjectRequest) .signatureDuration(Duration.ofHours(24)) .build(); PresignedPutObjectRequest presignedRequest = presigner.presignPutObject(presignRequest); return presignedRequest.url().toString(); } }
问题核心原因
你生成的是PUT操作的预签名URL,但实际发送的是POST请求。S3的签名算法与HTTP请求方法强绑定,方法不匹配会直接导致签名验证失败。
修复方案
方案一:客户端改用PUT请求(最简方案)
调整HTTP客户端的请求方法为PUT,匹配预签名URL的生成类型。例如用curl测试:
curl -X PUT -T "本地文件路径" "生成的预签名URL"
方案二:服务端生成POST预签名表单(若必须用POST)
如果业务要求必须用POST请求,需要修改服务端代码生成POST上传的预签名表单(S3 POST预签名逻辑与PUT不同,需生成表单字段而非单纯URL):
修改S3Service中的方法:
import java.util.Date; import java.util.HashMap; import java.util.Map; import software.amazon.awssdk.services.s3.model.PostPolicy; import software.amazon.awssdk.services.s3.presigner.model.PresignedPostRequest; public Map<String, String> generatePresignedPostUrl(String filename) { String key = "items/" + filename; Date expiration = new Date(System.currentTimeMillis() + 24 * 60 * 60 * 1000); PostPolicy policy = PostPolicy.builder(bucketName, expiration) .addCondition(KeyCondition.equals(key)) .build(); PresignedPostRequest presignedPost = presigner.presignPost(policy); Map<String, String> formFields = new HashMap<>(); formFields.put("url", presignedPost.url().toString()); formFields.putAll(presignedPost.fields()); return formFields; }
修改控制器返回表单信息:
@GetMapping("/presign-post/{filename}") public ResponseEntity<Map<String, String>> getPresignedPostUrl(@PathVariable String filename) { try { Map<String, String> presignedPostData = s3Service.generatePresignedPostUrl(filename); return ResponseEntity.ok(presignedPostData); } catch (Exception e) { return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(Map.of("error", "Failed to generate presigned POST data: " + e.getMessage())); } }
客户端需用POST方法提交表单,包含返回的所有字段和文件(文件字段名通常为file)。
额外排查点
- 确认生成预签名URL的凭证与S3验证时使用的凭证一致,避免多凭证混用。
- 确保请求时未修改URL中的任何参数(如签名、过期时间)。
- 检查文件名是否包含特殊字符,必要时进行URL编码。
内容的提问来源于stack exchange,提问作者Ariya nazari
相关产品推荐
相关产品推荐

