全部產品
Search
文件中心

Object Storage Service:在URL中包含V4簽名(推薦)

更新時間:Dec 06, 2024

除了通過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簽名描述

  • URL簽名樣本

    https://examplebucket.oss-cn-hangzhou.aliyuncs.com/exampleobject?x-oss-additional-headers=host&x-oss-credential=<AccessKeyId>%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=<AccessKeyId>%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

    指明衍生金鑰的參數集,格式如下:

    <AccessKeyId>/<date>/<region>/oss/aliyun_v4_request
    • AccessKeyId:填寫存取金鑰中的AccessKey ID。

    • date:填寫請求的日期,

    • region:填寫請求所在的Region。

    • oss:請求的服務名稱,固定為oss。

    • aliyun_v4_request:請求的版本說明,固定為aliyun_v4_request。

    x-oss-date

    字串

    20241203T034420Z

    簽名URL的起始時間,為避免時鐘誤差,允許向後位移15分鐘,格式為ISO8601。

    說明

    該時間用作StringToSign中的TimeStamp,取值必須與建立SigningKey中的Date是同一天。

    x-oss-expires

    整數

    3600

    簽名URL的有效時間長度,單位為秒(s)。最小值為1,最大值為 604800(即7 天)。

    x-oss-additional-headers

    字串

    host

    除預設要加入簽名的Header以外,指定其他也需要簽名的Header。例如:添加簽名host,並禁止修改請求的網域名稱。

    該欄位的構建方法說明如下:

    • 額外頭部中的所有Header均要求小寫。

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

    • 以分號串連數組中的元素,擷取字串。

    x-oss-signature

    字串

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

    用於簽名認證的描述資訊。x-oss-signature不參與簽名計算。

    x-oss-security-token

    字串

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

    STS安全性權杖。僅當使用STS構造URL簽名時,才需要設定此參數。

簽名計算過程

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,則只需要放置Null 字元串“”,末尾仍然需要有分行符號。

  • 如果簽名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);

    參數說明如下:

    參數

    類型

    是否必選

    樣本

    說明

    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=<AccessKeyId>%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

    上一步驟構造出來的字串。

  • 樣本

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

步驟3:計算Signature

使用簽名金鑰組待簽名字串進行計算。

  1. 計算SigningKey。

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

    HEX(HMAC-SHA256(SigningKey, StringToSign))

簽名計算樣本

以建立一個預簽名URL,分享給其他使用者上傳資料為例,示範如何在URL包含V4簽名。

  • 計算參數樣本

    參數

    AccessKeyId

    accesskeyid

    AccessKeySecret

    accesskeysecret

    Bucket

    examplebucket

    Object

    exampleobject

    Region

    cn-hangzhou

  • GetObject

    https://examplebucket.oss-cn-hangzhou.aliyuncs.com/exampleobject?x-oss-additional-headers=host&x-oss-credential=<AccessKeyId>%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
    host:examplebucket.oss-cn-hangzhou.aliyuncs.com

在URL包含V4簽名的步驟如下:

  1. 構造CanonicalRequest。

    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);
  2. 構造待簽名字串(StringToSign)。

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

    1. 計算SigningKey。

      說明

      為方便可讀,如下為SigningKey經Base64編碼後的字串。

       dateKey = hmacsha256(("aliyun_v4" + accesskeysecret).getBytes(), dateStr);
       dateRegionKey = hmacsha256(dateKey, "cn-hangzhou");
       dateRegionServiceKey = hmacsha256(dateRegionKey, "oss");
       signingKey = hmacsha256(dateRegionServiceKey, "aliyun_v4_request");
    2. 計算Signature。

      result = hmacsha256(signingKey, stringToSign);
      String signature = BinaryUtil.toHex(result);
      signature:eee300fa39f52127a02af5f9bb86c0fd8b6776fc19101d9a6a7982c9d0edcc04
  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);

簽名計算完整範例程式碼

使用上述計算參數樣本,通過Java範例程式碼示範簽名計算的完整過程。

重要

此樣本僅供示範URL計算簽名。如需使用此樣本,請將樣本參數替換為您所需參數,如:請將Bucket路徑替換為您的Bucket路徑;地區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=<AccessKeyId>%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
說明