You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在Google Cloud中使用Rust生成签名URL遇阻求助

Google Cloud Storage签名URL实现问题(Rust)

我尝试在Google Cloud中实现签名URL功能,但始终无法成功,官方文档较为复杂。以下是我用Rust编写的尝试代码:

fn signed_url(
    method: &str,
    object: &str,
) -> Result<String, Box<dyn std::error::Error>> {
    let expiration = Utc::now() + Duration::hours(1);
    let expiration = expiration.timestamp();

    let url_path = format!("/{}/{}", &env_vars().google_cloud_storage_bucket, object);
    let canonical_request = format!(
        "{method}\n{url_path}\n\nhost:storage.googleapis.com\nx-goog-algorithm:GOOG4-RSA-SHA256\nx-goog-credential:{}\nx-goog-date:{}\nx-goog-expires:{}\n\nhost;x-goog-algorithm;x-goog-credential;x-goog-date;x-goog-expires\nUNSIGNED-PAYLOAD", 
        env_vars().google_cloud_client_email, 
        Utc::now().format("%Y%m%dT%H%M%SZ"), 
        expiration
    );

    let rsa = Rsa::private_key_from_pem(env_vars().google_cloud_private_key.as_bytes())?;
    let pkey = PKey::from_rsa(rsa)?;
    let mut signer = Signer::new(MessageDigest::sha256(), &pkey)?;
    signer.update(canonical_request.as_bytes())?;
    let signature = signer.sign_to_vec()?;
    let encoded_signature = URL_SAFE.encode(signature);

    let signed_url = format!(
        "https://storage.googleapis.com{}?X-Goog-Algorithm=GOOG4-RSA-SHA256&X-Goog-Credential={}&X-Goog-Date={}&X-Goog-Expires={}&X-Goog-SignedHeaders=host&X-Goog-Signature={}",
        encode(&url_path),
        encode(&env_vars().google_cloud_client_email),
        Utc::now().format("%Y%m%dT%H%M%SZ"),
        expiration,
        encoded_signature
    );

    Ok(signed_url)
}

问题分析与修正方案

你的代码存在几个关键错误,导致签名URL验证失败:

  • 凭证格式错误:X-Goog-Credential并非直接使用客户端邮箱,而是需要遵循[客户端邮箱]/[日期(YYYYMMDD)]/[存储桶区域]/storage/goog4_request的格式,缺少区域和请求类型会导致验证不通过。
  • 时间不一致:生成规范请求和最终签名URL时两次调用Utc::now(),可能导致时间戳偏差,触发签名过期或无效的错误。
  • 私钥格式问题:环境变量中存储的私钥通常会把换行符转义为\\n,直接传入PEM解析会失败,需要先还原真实换行符。
  • 区域未指定:签名URL需要匹配存储桶所在的区域,未指定会导致签名逻辑不匹配。

修正后的代码

use chrono::{Utc, Duration};
use openssl::{pkey::PKey, rsa::Rsa, sign::Signer, hash::MessageDigest};
use urlencoding::{encode, URL_SAFE};
use std::env;

// 简化环境变量获取逻辑,实际项目中可替换为你的配置方式
struct EnvVars {
    google_cloud_storage_bucket: String,
    google_cloud_client_email: String,
    google_cloud_private_key: String,
}

fn env_vars() -> EnvVars {
    EnvVars {
        google_cloud_storage_bucket: env::var("GOOGLE_CLOUD_STORAGE_BUCKET").unwrap(),
        google_cloud_client_email: env::var("GOOGLE_CLOUD_CLIENT_EMAIL").unwrap(),
        google_cloud_private_key: env::var("GOOGLE_CLOUD_PRIVATE_KEY").unwrap(),
    }
}

fn signed_url(
    method: &str,
    object: &str,
) -> Result<String, Box<dyn std::error::Error>> {
    let vars = env_vars();
    let now = Utc::now();
    // 统一时间戳,避免多次调用导致的偏差
    let date_full = now.format("%Y%m%dT%H%M%SZ").to_string();
    let date_short = now.format("%Y%m%d").to_string();
    // 替换为你的存储桶实际区域,例如us-central1、asia-east1等
    let region = "us-central1";
    let expiration = (now + Duration::hours(1)).timestamp();

    let url_path = format!("/{}/{}", vars.google_cloud_storage_bucket, object);
    // 构造正确的凭证格式
    let credential = format!(
        "{}/{}/{}/storage/goog4_request",
        vars.google_cloud_client_email, date_short, region
    );

    let canonical_request = format!(
        "{method}\n{url_path}\n\nhost:storage.googleapis.com\nx-goog-algorithm:GOOG4-RSA-SHA256\nx-goog-credential:{}\nx-goog-date:{}\nx-goog-expires:{}\n\nhost;x-goog-algorithm;x-goog-credential;x-goog-date;x-goog-expires\nUNSIGNED-PAYLOAD",
        credential, date_full, expiration
    );

    // 还原私钥中的换行符
    let private_key = vars.google_cloud_private_key.replace("\\n", "\n");
    let rsa = Rsa::private_key_from_pem(private_key.as_bytes())?;
    let pkey = PKey::from_rsa(rsa)?;
    let mut signer = Signer::new(MessageDigest::sha256(), &pkey)?;
    signer.update(canonical_request.as_bytes())?;
    let signature = signer.sign_to_vec()?;
    let encoded_signature = URL_SAFE.encode(signature);

    let signed_url = format!(
        "https://storage.googleapis.com{}?X-Goog-Algorithm=GOOG4-RSA-SHA256&X-Goog-Credential={}&X-Goog-Date={}&X-Goog-Expires={}&X-Goog-SignedHeaders=host&X-Goog-Signature={}",
        encode(&url_path),
        encode(&credential),
        date_full,
        expiration,
        encoded_signature
    );

    Ok(signed_url)
}

额外注意事项

  • 确保存储桶所在区域与代码中指定的region一致,可在Google Cloud控制台查看存储桶详情获取区域信息。
  • 验证私钥是否为完整的PEM格式,开头为-----BEGIN PRIVATE KEY-----,结尾为-----END PRIVATE KEY-----。
  • 测试时可缩短过期时间(比如5分钟),方便快速验证签名是否有效。

内容的提问来源于stack exchange,提问作者Oliver Dixon

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 17:18:11