除了通过HTTP请求头部的Authorization字段进行签名授权外,您还可以生成一个包含签名和其他必要请求信息的预签名URL。通过这种方式,您可以在不透露访问凭证的情况下,授予第三方在特定有效期内对OSS资源的访问权限。本文介绍如何使用V4签名算法实现在URL中包含签名。
SDK签名实现
OSS SDK已实现自动完成V4签名。推荐采用OSS SDK的方式发起请求,可以免去手动签名的过程。如果您想了解特定语言的签名实现,请参考OSS SDK的代码。OSS SDK签名实现的文件请参见下表。
| SDK | 客户端初始化示例 | 签名实现 | 
| Java | ||
| PHP | ||
| Node.js | ||
| Browser.js | ||
| Python | ||
| Go | ||
| C# | ||
| Swift | ||
| Objective-C | ||
| C++ | ||
| 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签名时,才需要设置此参数。 
签名计算过程
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
步骤2:构造待签名字符串(StringToSign)
步骤3:计算Signature
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相关操作
- 当生成URL签名的文件类型为.txt时,在线预览可能会出现乱码问题。具体操作,请参见在线预览.txt文件出现乱码问题。 
- 常见语言使用SDK自动生成签名URL用于文件上传与下载,请参见使用预签名URL上传文件与使用预签名URL下载或预览文件。