Apache Reqsign 0.20.7 构建并运行示例应用指南

Apache Reqsign 0.20.7 构建并运行示例应用指南(Rust 请求签名库使用示例):从零创建 Cargo 工程、完整 Cargo.toml、AWS SigV4 签名头生成与发送示例源码、编译运行命令与真实回显(authorization/x-amz-date/x-amz-content-sha256 签名头与 200 OK 响应)。

Apache ReqsignApache Reqsign2026/10/13

Apache Reqsign 0.20.7 构建并运行示例应用指南

目标:从零构建并运行一个基于 Apache Reqsign 0.20.7 的最小请求签名应用(Rust 库使用示例)。给出完整 Cargo.toml、示例 Rust 源码、编译与运行命令及真实回显:程序为 AWS S3 请求生成 SigV4 签名头(authorization / x-amz-date / x-amz-content-sha256),并把签名后的请求发送到本地 HTTP 服务验证,返回 200 OK。所有命令与输出均在 Ubuntu 22.04.5 + rustc 1.99.0 环境实测通过。

一、环境信息

  • 操作系统:Ubuntu 22.04.5 LTS
  • Rust 工具链:rustc 1.99.0 / cargo 1.99.0(rustup stable)
  • Reqsign 版本:0.20.7(feature:aws-v4,依赖 tokio 1 / reqwest 0.12 / http 1)
  • 示例工程目录:/root/reqsign-demo/
  • 本地验证服务:python3 http.server(监听 127.0.0.1:8000,提供 /my-bucket/demo.txt)
  • 演示账号:root

二、前置条件

  • 已按《Apache Reqsign 0.20.7 安装指南》安装 Rust 工具链与系统依赖(pkg-config、libssl-dev),并确认版本:

    source "$HOME/.cargo/env"
    rustc --version
    cargo --version
    
  • 实测回显需为 rustc 1.99.0 / cargo 1.99.0(reqsign 0.20.7 要求 ≥ 1.86.0)。

三、工程结构

标准 Cargo 布局,仅一个 Cargo.toml 与一个主文件:

reqsign-demo/
├── Cargo.toml
└── src/main.rs
mkdir -p /root/reqsign-demo/src

四、完整 Cargo.toml

[package]
name = "reqsign-demo"
version = "0.1.0"
edition = "2021"

[dependencies]
reqsign = { version = "0.20.7", features = ["aws-v4"] }
tokio = { version = "1", features = ["rt-multi-thread", "macros"] }
reqwest = "0.12"
http = "1"

💡 说明:reqsign 开启 aws-v4 feature 后提供 reqsign::aws 模块(AWS SigV4 签名);tokio 提供异步运行时;reqwest 用于把签名后的请求真实发出;http 用于构造请求(签名接口操作的是 http::request::Parts)。

五、示例源码

创建 /root/reqsign-demo/src/main.rs,其完成六步:静态凭据提供者 → 组装签名器 → 构造待签名请求 → 执行 SigV4 签名 → 打印签名头 → 发送请求到本地 HTTP 服务验证。

示例源码:

use reqsign::aws::{RequestSigner, StaticCredentialProvider, EMPTY_STRING_SHA256};
use reqsign::{Context, Signer};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 1) 静态凭据提供者(演示密钥,非真实 AWS 凭据)
    let cred = StaticCredentialProvider::new(
        "AKIDEXAMPLE",
        "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
    );

    // 2) 上下文 + AWS SigV4 请求签名器(服务 s3 / 区域 us-east-1)
    let ctx = Context::new();
    let signer = Signer::new(ctx, cred, RequestSigner::new("s3", "us-east-1"));

    // 3) 构造待签名请求(指向本地 http.server)
    let target = "http://127.0.0.1:8000/my-bucket/demo.txt";
    let req = http::Request::get(target)
        .header("x-amz-content-sha256", EMPTY_STRING_SHA256)
        .body(())
        .unwrap();
    let (mut parts, _) = req.into_parts();

    // 4) 执行 AWS SigV4 签名
    signer.sign(&mut parts, None).await?;

    // 5) 打印签名后的请求头
    println!("== 签名后的请求头 ==");
    for name in ["authorization", "x-amz-date", "x-amz-content-sha256"] {
        match parts.headers.get(name) {
            Some(v) => println!("{}: {}", name, v.to_str()?),
            None => println!("{}: <missing>", name),
        }
    }

    // 6) 用签名后的请求头重建 reqwest 请求,发送到本地 http.server 验证
    let client = reqwest::Client::new();
    let mut builder = client
        .request(parts.method.clone(), parts.uri.to_string())
        .body(reqwest::Body::from(""));
    for (name, value) in &parts.headers {
        builder = builder.header(name, value);
    }
    let resp = client.execute(builder.build()?).await?;
    println!("== 本地 http.server 响应 ==");
    println!("status: {}", resp.status());
    Ok(())
}

💡 实现要点:

  • 签名接口 signer.sign(&mut parts, None) 操作的是 http::request::Parts,成功时只改写其中的 uri 与 headers,方法/版本/扩展保持不变。
  • EMPTY_STRING_SHA256 是空字符串的 SHA256(e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855),用于无请求体场景;有请求体时应先计算 body 的 SHA256 再写入 x-amz-content-sha256。
  • reqwest::Request 没有 from_parts 构造函数,把签名后的 Parts 转发时用 client.request(method, uri) 配合 .headers() / .body() 重建(实测踩坑记录,见第九节 Q3)。
  • 演示密钥 AKIDEXAMPLE 来自 AWS 官方文档示例,仅用于演示。

六、编译与运行

source "$HOME/.cargo/env"
cd /root/reqsign-demo

# 编译
cargo build
# 运行
cargo run

七、运行结果解读

编译成功(Finished \dev` profile [unoptimized + debuginfo] target(s) in 1.58s`),程序真实输出(退出码 0):

    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.11s
     Running `target/debug/reqsign-demo`
== 签名后的请求头 ==
authorization: AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20261012/us-east-1/s3/aws4_request, SignedHeaders=host;x-amz-content-sha256;x-amz-date, Signature=37962216ce11dc6ee635a8aac517e295cb23b0e6856a687141d085e31901c711
x-amz-date: 20261012T164829Z
x-amz-content-sha256: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
== 本地 http.server 响应 ==
status: 200 OK

结果解读:

  • authorization 头:AWS SigV4 规范格式。以本次实测值为例:AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20261012/us-east-1/s3/aws4_request, SignedHeaders=host;x-amz-content-sha256;x-amz-date, Signature=37962216ce11dc6ee635a8aac517e295cb23b0e6856a687141d085e31901c711。其中 Credential 段依次为 访问密钥ID/签名日期/区域/服务/aws4_request(日期 20261012、区域 us-east-1、服务 s3 来自签名器配置);SignedHeaders=host;x-amz-content-sha256;x-amz-date 列出参与签名的请求头;Signature 为 HMAC-SHA256 派生签名值。
  • x-amz-date:签名时间(UTC,格式如 20261012T164829Z,即 年4位月2位日2位 T 时2位分2位秒2位 Z),随运行时刻变化,本次实测为 20261012T164829Z。
  • x-amz-content-sha256:请求体的 SHA256,空请求体即 e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855。
  • status: 200 OK:签名后的请求被本地 HTTP 服务正常接收并返回文件内容,说明签名头完整可传输;在真实云服务上,服务端会用相同算法校验签名头,校验通过才会放行请求。

⚠️ 说明:x-amz-date 与 Signature 每次运行都会因当前时间不同而变化,请以实际输出为准;Credential 中的日期段与 x-amz-date 日期段保持一致即说明签名过程正确。

八、注意事项

⚠️ 注意:

  1. 必须使用 rustup 安装的 Rust 工具链(≥ 1.86.0);apt 版 cargo 1.75.0 无法编译(见第九节 Q1)。
  2. 签名前必须保证请求 URI 已是规范形态(含 authority),reqsign 不会代为做通用 URI 编码;路径/查询中属于数据的字节需自行百分号编码。
  3. 静态凭据只适合演示/测试;生产建议使用 DefaultCredentialProvider(从环境变量、~/.aws/credentials 等自动加载,详见《配置参数详解》篇)。
  4. 无请求体请求必须携带 x-amz-content-sha256: EMPTY_STRING_SHA256,否则签名与服务端校验会不一致。

九、常见问题

Q1:编译报 feature edition2024 is required? apt 版 cargo 1.75.0 不支持新版依赖的 edition2024。用 source "$HOME/.cargo/env" 切换到 rustup 工具链(实测 1.99.0)后重跑 cargo build。

Q2:运行报凭据错误 / 签名失败? 确认凭据提供者已正确配置:StaticCredentialProvider::new(access_key, secret_key) 的两个参数不能为空;使用环境变量凭据时须设置 AWS_ACCESS_KEY_ID 与 AWS_SECRET_ACCESS_KEY。

Q3:reqwest::Request::from_parts 不存在 / uri 无法传入 client.request? reqwest 0.12 的 Request 没有 from_parts 构造函数;client.request(method, url) 的 url 参数支持 String(IntoUrl),把 http::Uri 转成字符串传入,再逐个 .header() 拷贝签名头后 .build()。

Q4:签名后的请求发送到真实云服务返回 403? 常见原因:服务器时间偏差过大(签名时间校验失败)、凭据无权限、区域/服务名与资源不符。先核对 x-amz-date 是否为 UTC 当前时间,再核对 RequestSigner::new(service, region) 与目标服务一致。

十、参考链接

  • Reqsign 官方文档:https://docs.rs/reqsign
  • crates.io:https://crates.io/crates/reqsign
  • AWS SigV4 签名规范:https://docs.aws.amazon.com/general/latest/gr/signature-version-4.html
  • 源码仓库:https://github.com/apache/opendal-reqsign