在URL中包含V4签名(推荐)

除了通过HTTP请求头部的Authorization字段进行签名授权外,您还可以生成一个包含签名和其他必要的请求信息的预签名URL。通过这种方式,您可以在不透露访问凭证的情况下,授予第三方在特定有效期内对OSS资源的访问权限。本文介绍如何使用V4签名算法实现在URL中包含签名。

SDK签名实现

OSS SDK已实现自动完成V4签名。推荐采用OSS SDK的方式发起请求,可以免去手动签名的过程。如果您想了解具体语言的签名实现,请参考OSS SDK的代码。OSS SDK签名实现的文件请参见下表。

SDK

客户端初始化示例

签名实现

Java

初始化

OSSV4Signer.java

PHP

初始化

SignerV4.php

Node.js

初始化

signatureUrlV4.js

Browser.js

初始化

Python

初始化

auth.py

Go

初始化

v4.go

C++

初始化

SignerV4.cc

C

初始化

oss_auth.c

URL签名描述

  • 示例

    https://examplebucket.oss-cn-hangzhou.aliyuncs.com/exampleobject?x-oss-signature-version=OSS4-HMAC-SHA256&x-oss-credential=<AccessKeyId>/20231203/cn-hangzhou/oss/aliyun_v4_request&x-oss-date=20231203T121212Z&x-oss-expires=86400&x-oss-additional-headers=host&x-oss-signature=<signature-to-be-calculated>

    上述URL中的x-oss-credential的值为方便展示各个字段的参数,使用正斜线(/)进行分隔。在实际请求时,需要对URL中的/执行UrlEncode,即转化成%2F,示例如下:

    &x-oss-credential=<AccessKeyId>%2F20231203%2Fcn-hangzhou%2Foss%2Faliyun_v4_request
  • URL中的QueryString参数说明

    参数

    类型

    是否必选

    示例值

    说明

    x-oss-signature-version

    字符串

    OSS4-HMAC-SHA256

    指定签名的版本和算法,固定值为OSS4-HMAC-SHA256。

    x-oss-credential

    字符串

    LTAI********************/20231203/cn-hangzhou/oss/aliyun_v4_request

    指明派生密钥的参数集,格式如下:

    <AccessKeyId>/<date>/<region>/oss/aliyun_v4_request
    • AccessKeyId:填写访问密钥中的AccessKey ID。

    • date:填写请求的日期,

    • region:填写请求所在的Region。

    • oss:请求的服务名称,固定为oss。

    • aliyun_v4_request:请求的版本说明,固定为aliyun_v4_request。

    x-oss-date

    字符串

    20231203T121212Z

    签名URL的起始时间,为避免时钟误差,允许向后偏移15分钟,格式为ISO8601。

    说明

    该时间用作StringToSign中的TimeStamp,取值必须与创建SigningKey中的Date是同一天。

    x-oss-expires

    整数

    3600

    签名URL的有效时长,单位为秒(s)。最小值为1,最大值为 604800(即7 天)。

    x-oss-additional-headers

    字符串

    host

    除默认要加入签名的Header以外,指定其他需要签名的Header。建议将请求中使用的所有Header都加入签名。

    该字段的构建方法说明如下:

    • 额外头部中的所有Header均要求小写。

    • 按照字典序升序排列额外头部中的所有Header。

    • 以分号连接数组中的元素,获取字符串。

    x-oss-signature

    字符串

    77Dv****************

    用于签名认证的描述信息。x-oss-signature不参与签名计算。

    x-oss-security-token

    字符串

    CAIS********************************

    STS安全令牌。仅当使用STS构造URL签名时,才需要设置此参数。

签名计算过程

image

URL签名计算方法与Header签名计算方法基本类似。但由于签名URL使用方法的不同,与Header签名计算仍存在以下差异:

  • 签名URL的方式不包含描述payload的Header(x-oss-content-sha256)。原因是创建预签名时,无法预估实际传输的payload。因此,在URL签名过程中,直接使用UNSIGNED-PAYLOAD。

  • 如果签名URL的QueryParameter与待签名Header中的Key相同但Value不同的情况,会出现报错。同一个Key有多个Value的情况,会同时比较Key对应的所有Value,如果Value不一致也会出现报错。

  • 签名URL中,如果通过STS访问OSS,必须在QueryString中添加x-oss-security-token。

  • QueryString中的x-oss-signature不参与签名。

步骤1:构造CanonicalRequest

按照OSS签名定义的规范,对请求进行格式化。

格式

HTTP Verb + "\n" +
Canonical URI + "\n" +
Canonical Query String + "\n" +
Canonical Headers + "\n" +
Additional Headers + "\n" +
Hashed PayLoad

各参数说明如下:

参数

类型

是否必选

示例

说明

HTTP Verb

枚举值

PUT

HTTP请求的Method,包含PUT、GET、POST、HEAD、DELETE、OPTIONS等。

Canonical URI

字符串

/examplebucket/exampleobject

URI进行UriEncode后的字符串,其中正斜线(/)不需要编码。

  • 如果URI中不包含QueryString,则从正斜线(/)开始到URI末尾。

  • 如果URI中包含QueryString,则从正斜线(/)开始到问号(?)结束。

根据请求URI包含的资源有差异,Canonical URI的填写方法说明如下:

  • 如果请求的URI中既包含Bucket也包含Object,则Canonical URI填写示例为

    /examplebucket/exampleobject

  • 如果请求的URI中只包含Bucket不包含Object,则Canonical URI填写示例为/examplebucket/

  • 如果请求的URI中不包含Bucket只包含Object,则Canonical URI填写示例为/

Canonical Query String

字符串

UriEncode("marker") + "=" + UriEncode("someMarker") + "&" + UriEncode("max-keys") + "=" + UriEncode("20") + "&" + UriEncode("prefix") + "=" + UriEncode("somePrefix")

针对QueryString执行UriEncode后的字符串,单独对key和value进行编码。

  • 按QueryString的key进行排序,先编码,再排序。如果有多个相同的key,按照原来添加的顺序放置即可。

  • 只有key没有value的情况下,只添加 key即可。

  • 如果没有QueryString,则只需要放置空字符串“”,末尾仍然需要有换行符。

  • 如果签名URL的QueryParameter与待签名Header中的Key相同但Value不同的情况,会出现报错。同一个Key有多个Value的情况,会同时比较Key对应的所有Value,如果Value不一致也会出现报错。

Canonical Headers

字符串

host:cname.com
x-oss-content-sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
x-oss-date:20231203T121212Z

对请求Header的列表格式化后的字符串,Canonical Headers末尾仍需要添加换行符。

  • 单个Header中的key和value通过冒号:分隔,Header与Header之间通过换行符分隔。

  • Header的key必须小写,value必须经过Trim(去除头尾的空格)。

  • 按Header中key的字典序进行排列。

  • 请求时间通过x-oss-date来描述,要求格式必须是FormatISO8601(示例值为20231203T121212Z)。

  • 签名URL的方式不包含描述payload的Header(x-oss-content-sha256)。原因是创建预签名时,无法预估实际传输的payload。因此,在URL签名过程中,直接使用UNSIGNED-PAYLOAD。

Canonical Headers包含以下两类:

  • AdditionalHeader指定必须存在且参与签名的Header

  • 如果存在则加入签名的Header包括:

    • Content-Type

    • Content-MD5

    • x-oss-*

Additional Headers

字符串

content-length;host

用于指定需要加入签名的Header。所有的Header必须是小写,且按照字典序排列。

Hashed PayLoad

字符串

UNSIGNED-PAYLOAD

仅支持UNSIGNED-PAYLOAD。

示例

"GET" | "PUT" | ... + "\n" +
UriEncode(<Resource>) + "\n" +
UriEncode(<QueryParam1>) + "=" + UriEncode(<Value>) + "&" + UriEncode(<QueryParam2>) + "\n" +
Lowercase(<HeaderName1>) + ":" + Trim(<value>) + "\n" + Lowercase(<HeaderName2>) + ":" + Trim(<value>) + "\n" + "\n"
Lowercase(<AdditionalHeaderName1>) + ";" + Lowercase(<AdditionalHeaderName2>) + "\n" +
UNSIGNED-PAYLOAD

步骤2:构造待签名字符串(StringToSign)

将格式化后的请求进行统一的计算处理,得到待签名的字符串。

  • 格式

    "OSS4-HMAC-SHA256" + "\n" +
    TimeStamp + "\n" +
    Scope + "\n" +
    Hex(SHA256Hash(<CanonicalRequest>))

    参数说明如下:

    参数

    类型

    是否必选

    示例

    说明

    OSS4-HMAC-SHA256

    枚举值

    OSS4-HMAC-SHA25

    指明使用的签名哈希算法,取值必须是OSS4-HMAC-SHA256。

    TimeStamp

    字符串

    20231203T121212Z

    指明当前的UTC时间,格式必须是 ISO8601。

    Scope

    字符串

    20231203/cn-hangzhou/oss/aliyun_v4_request

    指明获取Region派生密钥的参数集,格式如下:

    <SignDate>/<Region>/oss/aliyun_v4_request
    • SignDate:填写请求的日期。

    • Region:填写请求所在的Region。

    • oss:请求的服务名称,固定为oss。

    • aliyun_v4_request:请求的版本说明,固定为aliyun_v4_request。

    CanonicalRequest

    字符串

    PUT

    /examplebucket/exampleobject

    x-oss-additional-headers=host&x-oss-credential=accesskeyid%2F20231203%2Fcn-hangzhou%2Foss%2Faliyun_v4_request&x-oss-date=20231203T121212Z&x-oss-expires=86400&x-oss-signature-version=OSS4-HMAC-SHA256

    host:examplebucket.oss-cn-hangzhou.aliyuncs.com

    x-oss-meta-author:alice

    x-oss-meta-magic:abracadabra

    host

    UNSIGNED-PAYLOAD

    上一步骤构造出来的字符串。

  • 示例

    "OSS4-HMAC-SHA256" + "\n" +
    FormatISO8601 + "\n" +
    20231203/cn-hangzhou/oss/aliyun_v4_request + "\n" +
    Hex(SHA256Hash(<CanonicalRequest>))

步骤3:计算Signature

使用签名密钥对待签名字符串进行计算。

  1. 计算SigningKey。

    HMAC-SHA256(HMAC-SHA256(HMAC-SHA256(HMAC-SHA256("aliyun_v4" + SK, Date), Region), "oss"), "aliyun_v4_request");
  2. 计算Signature。

    HEX(HMAC-SHA256(SigningKey, StringToSign))

签名计算示例

以创建一个预签名URL,分享给其他用户上传数据为例,演示如何在URL包含V4签名。

  • 计算示例参数说明

    参数

    AccessKeyId

    accesskeyid

    AccessKeySecret

    accesskeysecret

    Timestamp

    20231203T121212Z

    Bucket

    examplebucket

    Object

    exampleobject

    Region

    cn-hangzhou

  • PutObject

    https://examplebucket.oss-cn-hangzhou.aliyuncs.com/exampleobject?x-oss-signature-version=OSS4-HMAC-SHA256&x-oss-credential=accesskeyid/20231203/cn-hangzhou/oss/aliyun_v4_request&x-oss-date=20231203T121212Z&x-oss-expires=86400&x-oss-additional-headers=host&x-oss-signature=<signature-to-be-calculated>
    Host: examplebucket.oss-cn-hangzhou.aliyuncs.com
    x-oss-meta-author: alice
    x-oss-meta-magic: abracadabra

在URL包含V4签名的步骤如下:

  1. 构造CanonicalRequest。

    PUT
    /examplebucket/exampleobject
    x-oss-additional-headers=host&x-oss-credential=accesskeyid%2F20231203%2Fcn-hangzhou%2Foss%2Faliyun_v4_request&x-oss-date=20231203T121212Z&x-oss-expires=86400&x-oss-signature-version=OSS4-HMAC-SHA256
    host:examplebucket.oss-cn-hangzhou.aliyuncs.com
    x-oss-meta-author:alice
    x-oss-meta-magic:abracadabra
    
    host
    UNSIGNED-PAYLOAD
  2. 构造待签名字符串(StringToSign)。

    OSS4-HMAC-SHA256
    20231203T121212Z
    20231203/cn-hangzhou/oss/aliyun_v4_request
    672d815902f04dd8aa90a558931f471cc7269d08a122a5e9028022d9f723332c
  3. 计算签名。

    1. 计算SigningKey。

      说明

      为方便可读,如下为SigningKey经Base64编码后的字符串。

      WVjaYR8lCj9YC5PUS2RSZQANYbuh9DhMFxjU1NtZKfc=
    2. 计算Signature。

      2c6c9f10d8950fb150290ef6f42570e33cd45d6a57ec7887de75fa2ec45b4c72
  4. 在URL中加入签名。

    https://examplebucket.oss-cn-hangzhou.aliyuncs.com?x-oss-additional-headers=host&x-oss-credential=accesskeyid%2F20231203%2Fcn-hangzhou%2Foss%2Faliyun_v4_request&x-oss-date=20231203T121212Z&x-oss-expires=86400&x-oss-signature=2c6c9f10d8950fb150290ef6f42570e33cd45d6a57ec7887de75fa2ec45b4c72&x-oss-signature-version=OSS4-HMAC-SHA256
    Host: examplebucket.oss-cn-hangzhou.aliyuncs.com
    x-oss-meta-author: alice
    x-oss-meta-magic: abracadabra

签名计算完整示例代码

以上述签名计算示例提供的参数为例,通过Java示例代码演示签名计算的完整过程。

import com.aliyun.oss.common.utils.BinaryUtil;
import org.apache.commons.codec.digest.DigestUtils;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.net.URL;

public class Demo {

    /**
     * 签名计算工具
     *
     * @return url
     */
    public static void main(String[] args) throws Exception {
        // 运行本代码示例之前,请确保已设置环境变量OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。
        String accesskeyid =  System.getenv().get("OSS_ACCESS_KEY_ID");
        String accesskeysecret =  System.getenv().get("OSS_ACCESS_KEY_SECRET");
        // 步骤1:构造CanonicalRequest。
        String canonicalRequest =
                "PUT\n" +
                        "/examplebucket/exampleobject\n" +
                        "x-oss-additional-headers=host&x-oss-credential="+accesskeyid+"%2F20231203%2Fcn-hangzhou%2Foss%2Faliyun_v4_request&x-oss-date=20231203T121212Z&x-oss-expires=86400&x-oss-signature-version=OSS4-HMAC-SHA256\n" +
                        "host:examplebucket.oss-cn-hangzhou.aliyuncs.com\n" +
                        "x-oss-meta-author:alice\n" +
                        "x-oss-meta-magic:abracadabra\n" +
                        "\n" +
                        "host\n" +
                        "UNSIGNED-PAYLOAD";

        // 步骤2:构造待签名字符串(StringToSign)。
        String stringToSign = "OSS4-HMAC-SHA256\n" +
                "20231203T121212Z\n" +
                "20231203/cn-hangzhou/oss/aliyun_v4_request\n" +
                DigestUtils.sha256Hex(canonicalRequest);

        // 步骤3:计算Signature。
        byte[] dateKey = hmacsha256(("aliyun_v4" + accesskeysecret).getBytes(), "20231203");
        byte[] dateRegionKey = hmacsha256(dateKey, "cn-hangzhou");
        byte[] dateRegionServiceKey = hmacsha256(dateRegionKey, "oss");
        byte[] signingKey = hmacsha256(dateRegionServiceKey, "aliyun_v4_request");

        byte[] result = hmacsha256(signingKey, stringToSign);
        String signature = BinaryUtil.toHex(result);
        System.out.println("signature:" + signature);

        // 步骤4:在URL中加入签名。
        String resourcePath = "exampleobject";
        String endpoint = "https://examplebucket.oss-cn-hangzhou.aliyuncs.com";
        String queryString = "x-oss-additional-headers=host&" +
                "x-oss-credential="+accesskeyid+"%2F20231203%2Fcn-hangzhou%2Foss%2Faliyun_v4_request&" +
                "x-oss-date=20231203T121212Z&" +
                "x-oss-expires=86400&" +
                "x-oss-signature=" + signature + "&" +
                "x-oss-signature-version=OSS4-HMAC-SHA256";

        String urlStr = endpoint + "/" + resourcePath + "?" + queryString;
        URL url = new URL(urlStr);
        System.out.println("url:" + url);
    }

    public static byte[] hmacsha256(byte[] key, String data) {
        try {
            // 初始化HMAC密钥规格,指定算法为HMAC-SHA256并使用提供的密钥。
            SecretKeySpec secretKeySpec = new SecretKeySpec(key, "HmacSHA256");

            // 获取Mac实例,并通过getInstance方法指定使用HMAC-SHA256算法。
            Mac mac = Mac.getInstance("HmacSHA256");
            // 使用密钥初始化Mac对象。
            mac.init(secretKeySpec);

            // 执行HMAC计算,通过doFinal方法接收需要计算的数据并返回计算结果的数组。
            byte[] hmacBytes = mac.doFinal(data.getBytes());

            return hmacBytes;
        } catch (Exception e) {
            throw new RuntimeException("Failed to calculate HMAC-SHA256", e);
        }
    }
}

返回结果如下:

signature:2c6c9f10d8950fb150290ef6f42570e33cd45d6a57ec7887de75fa2ec45b4c72
url:https://examplebucket.oss-cn-hangzhou.aliyuncs.com/exampleobject?x-oss-additional-headers=host&x-oss-credential=accesskeyid%2F20231203%2Fcn-hangzhou%2Foss%2Faliyun_v4_request&x-oss-date=20231203T121212Z&x-oss-expires=86400&x-oss-signature=2c6c9f10d8950fb150290ef6f42570e33cd45d6a57ec7887de75fa2ec45b4c72&x-oss-signature-version=OSS4-HMAC-SHA256