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

更新时间:
复制 MD 格式

除了通过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

初始化(Node.js SDK)

signatureUrlV4.js

Browser.js

初始化(Browser.js SDK)

Python

初始化(Python SDK V1)

auth.py

Go

配置客户端(Go SDK V1)

v4.go

C#

配置客户端

SignerV4.cs

Swift

配置客户端(Swift SDK)

SignerV4.swift

Objective-C

初始化(iOS SDK)

OSSV4Signer.m

C++

初始化(C++ SDK)

SignerV4.cc

C

初始化(C SDK)

oss_auth.c

URL签名描述

  • URL签名示例(V4)

    https://examplebucket.oss-cn-hangzhou.aliyuncs.com/exampleobject?x-oss-additional-headers=host&x-oss-credential=LTAI********************%2F20241203%2Fcn-hangzhou%2Foss%2Faliyun_v4_request&x-oss-date=20241203T034420Z&x-oss-expires=86400&x-oss-signature=70c542eaf652ac291c0c343d63ac24ede41c0526661d9d4c63c0906a2686160c&x-oss-signature-version=OSS4-HMAC-SHA256

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

    &x-oss-credential=LTAI********************%2F20241203%2Fcn-hangzhou%2Foss%2Faliyun_v4_request
  • URL中的QueryString参数说明

    参数

    类型

    是否必选

    示例值

    说明

    x-oss-signature-version

    字符串

    是

    OSS4-HMAC-SHA256

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

    x-oss-credential

    字符串

    是

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

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

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

    • date:填写请求的日期。

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

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

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

    x-oss-date

    字符串

    是

    20241203T034420Z

    签名URL的起始时间,格式为UTC,遵循ISO 8601标准。

    说明

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

    x-oss-expires

    整数

    是

    3600

    签名URL的有效期,即从x-oss-date开始计算,请求在多少秒内被视为有效,单位为秒(s)。

    • 使用长期访问密钥AccessKey生成签名URL,该字段取值要求:最小值为 1 秒,最大值为 604800秒( 7 天)。

    • 使用STS临时访问凭证生成签名URL,该字段取值要求:最小值为 1 秒,最大有效时长为 43200秒( 12 小时)。

    说明

    OSS服务器接收到请求的实际时间必须满足:(x-oss-date−15 分钟)≤OSS接收时间≤(x-oss-date+x-oss-expires)。

    • 如果当前OSS接收到请求的时间早于x-oss-date减去15分钟,则认为该请求无效(过早)。

    • 如果当前时间晚于x-oss-date加上x-oss-expires指定的秒数,则该请求也无效(过期)。

    x-oss-additional-headers

    字符串

    否

    host

    除默认要加入签名的Header以外,指定其他也需要签名的Header。例如:添加签名host,并禁止修改请求的域名。

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

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

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

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

    x-oss-signature

    字符串

    是

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

    仅用于签名认证的信息描述,不参与签名计算。

    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

枚举值

是

GET

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:

examplebucket.oss-cn-hangzhou.aliyuncs.com

x-oss-content-sha256:

eee300fa39f52127a02af5f9bb86c0fd8b6776fc19101d9a6a7982c9d0edcc04

x-oss-date:

20241203T034420Z

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

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

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

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

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

  • 签名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" | "GET" | ... + "\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" +
dateTimeStr + "\n" +
dateStr + "\n" +
DigestUtils.sha256Hex(canonicalRequest);

示例

String stringToSign = "OSS4-HMAC-SHA256\n" +
                dateTimeStr + "\n" +
                dateStr + "/cn-hangzhou/oss/aliyun_v4_request\n" +
                DigestUtils.sha256Hex(canonicalRequest);

参数说明如下:

参数

类型

是否必选

示例

说明

OSS4-HMAC-SHA256

枚举值

是

OSS4-HMAC-SHA256

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

dateTimeStr

字符串

是

20241203T034420Z

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

dateStr

字符串

是

20241203/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

字符串

是

GET

/examplebucket/exampleobject

x-oss-additional-headers=host&x-oss-credential=LTAI********************%2F20241203%2Fcn-hangzhou%2Foss%2Faliyun_v4_request&x-oss-date=20241203T034420Z&x-oss-expires=86400&x-oss-signature-version=OSS4-HMAC-SHA256

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

host

UNSIGNED-PAYLOAD

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

步骤3:计算Signature

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

  1. 计算SigningKey。

    HMAC-SHA256(HMAC-SHA256(HMAC-SHA256(HMAC-SHA256("aliyun_v4" + accesskeysecret).getBytes(), dateStr), Region), "oss"), "aliyun_v4_request");
  2. 计算Signature。

    BinaryUtil.toHex(HMAC-SHA256(SigningKey, StringToSign))

V4签名计算完整示例

此示例为通过Java示例代码演示V4签名计算,实现生成GET方法的URL签名的完整过程。此URL签名仅支持下载访问操作。

重要

使用以下示例代码时,请将代码中变量的取值替换为实际值。例如Canonical URI取值(/examplebucket/exampleobject)、Region取值(cn-hangzhou)等。

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;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.TimeZone;

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");
        // 获取当前时间并格式化为指定的 ISO8601 格式
        ZonedDateTime now = ZonedDateTime.now(TimeZone.getTimeZone("UTC").toZoneId());
        String dateStr = now.format(DateTimeFormatter.ofPattern("yyyyMMdd"));
        String dateTimeStr = now.format(DateTimeFormatter.ofPattern("yyyyMMdd'T'HHmmss'Z'"));
        // 步骤1:构造CanonicalRequest。
        String canonicalRequest =
                "GET\n" +
                        "/examplebucket/exampleobject\n" +
                        "x-oss-additional-headers=host&x-oss-credential=" + accesskeyid + "%2F" + dateStr + "%2Fcn-hangzhou%2Foss%2Faliyun_v4_request&x-oss-date=" + dateTimeStr + "&x-oss-expires=86400&x-oss-signature-version=OSS4-HMAC-SHA256\n" +
                        "host:examplebucket.oss-cn-hangzhou.aliyuncs.com\n" +
                        "\n" +
                        "host\n" +
                        "UNSIGNED-PAYLOAD";
        System.out.println("canonicalRequest:" + canonicalRequest);
        // 步骤2:构造待签名字符串(StringToSign)。
        String stringToSign = "OSS4-HMAC-SHA256\n" +
                dateTimeStr + "\n" +
                dateStr + "/cn-hangzhou/oss/aliyun_v4_request\n" +
                DigestUtils.sha256Hex(canonicalRequest);

        // 步骤3:计算Signature。
        byte[] dateKey = hmacsha256(("aliyun_v4" + accesskeysecret).getBytes(), dateStr);
        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 + "%2F" + dateStr + "%2Fcn-hangzhou%2Foss%2Faliyun_v4_request&" +
                "x-oss-date=" + dateTimeStr + "&" +
                "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:eee300fa39f52127a02af5f9bb86c0fd8b6776fc19101d9a6a7982c9d0edcc04
url:https://examplebucket.oss-cn-hangzhou.aliyuncs.com/exampleobject?x-oss-additional-headers=host&x-oss-credential=LTAI********************%2F20241203%2Fcn-hangzhou%2Foss%2Faliyun_v4_request&x-oss-date=20241203T032307Z&x-oss-expires=86400&x-oss-signature=eee300fa39f52127a02af5f9bb86c0fd8b6776fc19101d9a6a7982c9d0edcc04&x-oss-signature-version=OSS4-HMAC-SHA256

相关操作