阿里云OSS使用预签名URL上传文件,状态码返回403,报SignatureDoesNotMatch异常

1. 背景

  • 前端使用 Vue.js,后端使用 Springboot 和 aliyun-sdk-oss
  • 使用阿里云 OSS 使用预签名URL上传文件,异常返回 SignatureDoesNotMatch 错误码 0002-00000201。
  • 异常信息如下:
xml 复制代码
<?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>
    
    <!-- 省略部分内容 -->

    <EC>0002-00000201</EC>
    <RecommendDoc>https://api.aliyun.com/troubleshoot?q=0002-00000201</RecommendDoc>
</Error>

2. 排查

  1. 根据 api.aliyun.com/troubleshoo... 逐一确认,Endpoint,AccessKey ID,AccessKey Secret 等参数均无误,排除配置参数问题
  2. 查看文档 Java 示例。先运行 Java 示例先生成预签名 url,再运行 Java 示例上传文件,上传成功。将预签名 url 放到前端调试,上传失败,同样异常返回 SignatureDoesNotMatch 错误码 0002-00000201。基本判断问题在前端。
  3. 查看文档 browser.js 示例,意外发现线索。基本确认问题在 Content-Type 请求头上。

如果您使用 Browser.js 上传文件时遇到 403 签名不匹配错误,通常是因为浏览器会自动添加 Content-Type 请求头,而生成预签名 URL 时未指定该请求头,导致签名验证失败。为解决此问题,您需要在生成预签名 URL 时指定 Content-Type 请求头。

3. 处理

  1. 参考文档,在后端代码添加 StorageClass 和 ContentType
java 复制代码
public String generatePresignedUploadUrl(String fileName) {
 // 指定生成的预签名URL过期时间,单位为毫秒。本示例以设置过期时间为1小时为例。
        Date expiration = new Date(new Date().getTime() + 3600 * 1000L);
        GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(ossConfig.getBucketName(), fileName, HttpMethod.PUT);
        request.setExpiration(expiration);
        
        // 添加请求头
        Map<String, String> headers = new HashMap<>();
        // 指定StorageClass。
        headers.put(OSSHeaders.STORAGE_CLASS, StorageClass.Standard.toString());
        // 指定ContentType。
        headers.put(OSSHeaders.CONTENT_TYPE, "application/octet-stream");
        request.setHeaders(headers);
        
        URL url = ossClient.generatePresignedUrl(request);
        return url.toString();
}
  1. 参考文档,在前端代码添加相同的 StorageClass 和 ContentType
typescript 复制代码
      // 2. 使用预签名URL上传文件 - 使用PUT请求
      const uploadResponse = await fetch(presignedUrl, {
        method: 'PUT',
        body: file,
        headers: {
          'Content-Type': 'application/octet-stream',
          'x-oss-storage-class': 'Standard',
        },
      })
  1. 测试验证,上传成功。
相关推荐
拖孩4 分钟前
用 AI 重解千年观音灵签,做了一个微信小程序,每天摇一摇,命运给你回应
前端·后端·微信小程序
Python私教5 分钟前
AI Agent 可观测性不只是日志:一套可回放的多步执行链
人工智能·后端·python
Python私教18 分钟前
模型越强越不需要 Skills?我把 AI 编程能力拆成 4 层
人工智能·后端·python
名字还没想好☜1 小时前
Go 的 io.Reader/Writer 组合实战:io.Copy、TeeReader、MultiWriter 优雅处理数据流
开发语言·后端·golang·go·iphone
喵个咪2 小时前
GoWind Shop 架构剖析:一个 REST 请求穿越三服务 BFF 的七环链路
vue.js·后端·go
喵个咪2 小时前
GoWind Shop 安全设计:JWT 鉴权、Ent 行级隔离、防篡改审计与四个真实漏洞修复
vue.js·后端·go
喵个咪2 小时前
GoWind Shop:一个 proto 文件如何生成后端、前端、文档、校验六路代码
vue.js·后端·go
用户7783366132112 小时前
从 0 搭一个关键词排名监控:核心思路 + 可运行代码
后端·python·api
喵个咪2 小时前
GoWind Shop 出海实战:多语言商品内容怎么存、怎么录、怎么按 locale 取
vue.js·后端·go
郝学胜_神的一滴3 小时前
C++20 高级编程 001:从极简HelloWorld到现代类型体系全梳理
c++·后端