Skip to content

[bug] 阿里云 OSS 连接失败 (S3 兼容性问题 / 强制 Path-Style 导致 403) #796

@xianggau

Description

@xianggau

详细描述这个 Bug

问题描述

使用阿里云 OSS (Alibaba Cloud OSS) 作为 S3 图床/存储后端时,即使 AccessKey、SecretKey 和 Endpoint 配置正确,连接测试也会失败,返回 403 Forbidden

经过排查,原因是 NoteGen 在构造 S3 请求时强制使用了 Path-Style (https://endpoint/bucket),而阿里云 OSS 对这种请求格式的签名校验十分严格(或者需要特定的 Host 头配置),导致默认生成的签名无法通过校验。

复现步骤

  1. 打开 NoteGen 设置 -> 图床/存储设置。
  2. 选择 S3 兼容存储。
  3. 填入阿里云 OSS 配置:
    • AccessKeyId: LTAI... (有效密钥)
    • SecretAccessKey: ... (有效密钥)
    • Bucket: my-bucket-name
    • Region: oss-cn-beijing
    • Endpoint: https://oss-cn-beijing.aliyuncs.com
  4. 点击“测试连接”。

期望行为

连接成功 (HTTP 200)。

实际行为

连接失败,控制台或日志显示 HTTP 403 Forbidden。

技术分析

问题出在 src/lib/imageHosting/s3.ts 中的 testS3Connection 和其他请求构造逻辑。

NoteGen 目前的代码逻辑如下:

const endpoint = config.endpoint || `https://s3.${config.region}.amazonaws.com`;
const url = `${endpoint}/${config.bucket}`; // 强制 Path-Style

当 Endpoint 为 https://oss-cn-beijing.aliyuncs.com 时,请求 URL 变为:
https://oss-cn-beijing.aliyuncs.com/my-bucket-name

此时 Host 头是 oss-cn-beijing.aliyuncs.com。阿里云 OSS 收到此类请求时,可能无法正确解析 Bucket 或者认为签名不匹配。

相比之下,标准的 AWS SDK 或其他工具通常会自动使用 Virtual-Hosted Style:
https://my-bucket-name.oss-cn-beijing.aliyuncs.com

临时解决方案 (用户侧)

目前用户无法通过常规配置绕过此问题,除非将 Endpoint 修改为 Bucket 域名并将 Bucket 字段留空(但这会导致路径拼接错误)。

建议修复方案

建议在 src/lib/imageHosting/s3.ts 中增加对 Virtual-Hosted Style 的支持,或者自动检测阿里云 OSS Endpoint。

修改建议 (伪代码):

const endpoint = config.endpoint || `https://s3.${config.region}.amazonaws.com`;
let url;

// 简单的判断逻辑:如果 endpoint 包含 aliyuncs.com,使用 Virtual-Hosted Style
// 或者提供一个开关 forcePathStyle: boolean
if (endpoint.includes('aliyuncs.com') && !endpoint.includes(config.bucket)) {
    // 构造 Virtual-Hosted URL: https://bucket.region.aliyuncs.com
    // 注意:这里需要从 endpoint 中提取协议头,或者假设是 https
    const protocol = endpoint.split('://')[0];
    const domain = endpoint.split('://')[1];
    url = `${protocol}://${config.bucket}.${domain}`;
} else {
    url = `${endpoint}/${config.bucket}`;
}

附:复现脚本 (Node.js)

可以使用以下脚本验证该问题(证明 Credentials 是好的,只是 URL 构造方式导致了 403):

// repro-notegen.js
const https = require('https');
const crypto = require('crypto');

// 填入阿里云配置
const config = {
  accessKeyId: "YOUR_AK",
  secretAccessKey: "YOUR_SK", 
  region: "oss-cn-beijing",
  bucket: "YOUR_BUCKET",
  endpoint: "https://oss-cn-beijing.aliyuncs.com",
};

// ... (此处省略 AWS V4 签名实现代码,这部分代码证明了手动构造 Path-Style 请求会复现 403)

NoteGen 版本

v0.22.4

操作系统

Windows

报错日志

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions