How to upload files to OSS from a WeChat mini program
You can upload files such as images, documents, and videos from a WeChat mini program to Object Storage Service (OSS) for cloud storage and distribution.
Solution overview
The following describes the process of uploading a file from a WeChat mini program to OSS:
To upload files from a WeChat mini program to OSS, you can follow these two steps:
Configure the server side: On the server side, you create an ECS instance to obtain temporary access credentials from Security Token Service (STS). You then use these credentials to generate a signature that the WeChat mini program can use to upload files to OSS.
Configure the WeChat mini program: On the mini program platform, you configure the bucket domain name as a valid domain name for the WeChat mini program. This ensures that WeChat does not block requests from the mini program to OSS. On the WeChat mini program client, you implement the logic to obtain the signature from the ECS instance and use it to upload files to OSS.
Procedure
Step 1: Configure the server side
In a real-world deployment, if you already have an ECS server, you do not need to create a new ECS instance. You can proceed directly to Calculate the signature on the ECS server side.
Create and connect to an ECS instance.
Calculate the signature on the ECS server side.
ImportantThe server side provides two methods to obtain an STS temporary access credential and calculate a signature.
Obtain an STS temporary access credential and calculate a signature using an ECS instance that assumes a RAM role: The server side does not store AccessKey information. Instead, the ECS instance assumes a RAM role to access STS, obtain a temporary access credential, and calculate a signature. This method minimizes the risk of AccessKey pair leaks and offers higher security.
Obtain an STS temporary access credential and calculate a signature as a RAM user: The server side needs to store AccessKey information. It accesses STS using the RAM user's AccessKey pair and the ARN of the assumed RAM role, which are configured in the server's environment variables. It then obtains a temporary access credential and calculates a signature. This method is less secure.
Obtain an STS temporary access credential and calculate a signature by having an ECS instance assume a RAM role
NoteECS is a cloud server provided by Alibaba Cloud. The following code examples must be run in a cloud server environment. Local environments do not support these operations.
When you use ECS, you do not need to create a RAM user. You can simply grant a RAM role to the ECS instance. The instance can then assume the role to obtain an STS temporary access credential and calculate a signature.
Attach a RAM role to the ECS instance.
Calculate the signature on the server side.
Java
Use the following example to calculate the V4 signature on the Java server side. For the complete sample project, deploy upload_server.zip. Note that this sample project is built with JDK 23 and Spring Boot 3.4.0. You may need to adjust it based on your environment to ensure that the code runs as expected.
Configure dependencies.
<!-- https://mvnrepository.com/artifact/com.aliyun/credentials-java --> <dependency> <groupId>com.aliyun</groupId> <artifactId>credentials-java</artifactId> <version>0.3.4</version> </dependency> <dependency> <groupId>com.aliyun.kms</groupId> <artifactId>kms-transfer-client</artifactId> <version>0.1.0</version> </dependency> <dependency> <groupId>com.aliyun.oss</groupId> <artifactId>aliyun-sdk-oss</artifactId> <version>3.17.4</version> </dependency>API operation examples
package com.example.demo.controller; import com.example.demo.util.ECSGenerateSignature; import com.example.demo.util.RAMGenerateSignature; import com.fasterxml.jackson.core.JsonProcessingException; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class VxController { /** * Obtain temporary access credentials by having an ECS instance assume a RAM role, calculate the signature information, and return it to the mini program client. * @return * @throws JsonProcessingException */ @GetMapping("/generate_signature") public String generate_signature() throws JsonProcessingException { ECSGenerateSignature ecsGenerateSignature = new ECSGenerateSignature(); return ecsGenerateSignature.getSignature(); } }Sample signature information utility class.
package com.example.demo.util; import com.aliyun.credentials.models.CredentialModel; import com.aliyun.oss.common.auth.Credentials; import com.aliyun.oss.common.auth.CredentialsProvider; import com.aliyun.oss.common.auth.DefaultCredentials; import com.aliyun.oss.common.utils.BinaryUtil; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.commons.codec.binary.Base64; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import java.time.Instant; import java.time.ZoneId; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * Obtain an STS temporary access credential and calculate a signature by having an ECS instance assume a RAM role. */ public class ECSGenerateSignature { public String getSignature() throws JsonProcessingException { com.aliyun.credentials.models.Config config = new com.aliyun.credentials.models.Config(); config.setType("ecs_ram_role"); // This is a static field. Do not change it. config.setRoleName("roleName"); // Replace with the name of the RAM role attached to the ECS instance in step a. final com.aliyun.credentials.Client credentialsClient = new com.aliyun.credentials.Client(config); // Create an anonymous inner class that implements the CredentialsProvider interface to provide the credentials required for Alibaba Cloud OSS operations. CredentialsProvider credentialsProvider = new CredentialsProvider() { @Override public void setCredentials(Credentials credentials) { } @Override public Credentials getCredentials() { CredentialModel credential = credentialsClient.getCredential(); return new DefaultCredentials(credential.getAccessKeyId(), credential.getAccessKeySecret(), credential.getSecurityToken()); } }; String accessKeyId = credentialsProvider.getCredentials().getAccessKeyId(); // Get the AccessKey ID. String secretAccessKey = credentialsProvider.getCredentials().getSecretAccessKey(); // Get the AccessKey secret. String securityToken = credentialsProvider.getCredentials().getSecurityToken(); // Get the token. // Format the request date. long now = System.currentTimeMillis() / 1000; ZonedDateTime dtObj = ZonedDateTime.ofInstant(Instant.ofEpochSecond(now), ZoneId.of("UTC")); ZonedDateTime dtObjPlus3h = dtObj.plusHours(3); // Request time. DateTimeFormatter dtObj1Formatter = DateTimeFormatter.ofPattern("yyyyMMdd'T'HHmmss'Z'"); String dtObj1 = dtObj.format(dtObj1Formatter); // Request date. DateTimeFormatter dtObj2Formatter = DateTimeFormatter.ofPattern("yyyyMMdd"); String dtObj2 = dtObj.format(dtObj2Formatter); // Request expiration time. DateTimeFormatter expirationTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"); String expirationTime = dtObjPlus3h.format(expirationTimeFormatter); // Create the policy. // The example policy form fields list only the required fields. For more information about other fields, see https://help.aliyun.com/zh/oss/developer-reference/signature-version-4-recommend. ObjectMapper mapper = new ObjectMapper(); Map<String, Object> policy = new HashMap<>(); policy.put("expiration", expirationTime); List<Object> conditions = new ArrayList<>(); Map<String, String> bucketCondition = new HashMap<>(); bucketCondition.put("bucket", "bucketname"); // Replace <bucketname> with your actual bucket name. conditions.add(bucketCondition); Map<String, String> signatureVersionCondition = new HashMap<>(); signatureVersionCondition.put("x-oss-signature-version", "OSS4-HMAC-SHA256"); conditions.add(signatureVersionCondition); Map<String, String> credentialCondition = new HashMap<>(); credentialCondition.put("x-oss-credential", accessKeyId + "/" + dtObj2 + "/cn-hangzhou/oss/aliyun_v4_request"); // Replace <cn-hangzhou> with the region of your bucket. For example, for the China (Beijing) region, use cn-beijing. conditions.add(credentialCondition); Map<String, String> token = new HashMap<>(); token.put("x-oss-security-token", securityToken); conditions.add(token); Map<String, String> dateCondition = new HashMap<>(); dateCondition.put("x-oss-date", dtObj1); conditions.add(dateCondition); policy.put("conditions", conditions); String jsonPolicy = mapper.writeValueAsString(policy); // Construct the string-to-sign. String stringToSign = new String(Base64.encodeBase64(jsonPolicy.getBytes())); // Calculate the signing key. byte[] dateKey = hmacsha256(("aliyun_v4" + secretAccessKey).getBytes(), dtObj2); byte[] dateRegionKey = hmacsha256(dateKey, "cn-hangzhou"); // Replace <cn-hangzhou> with the region of your bucket. For example, for the China (Beijing) region, use cn-beijing. byte[] dateRegionServiceKey = hmacsha256(dateRegionKey, "oss"); byte[] signingKey = hmacsha256(dateRegionServiceKey, "aliyun_v4_request"); // Calculate the signature. byte[] result = hmacsha256(signingKey, stringToSign); String signature = BinaryUtil.toHex(result); Map<String, String> messageMap = new HashMap<>(); messageMap.put("security_token", securityToken); messageMap.put("signature", signature); messageMap.put("x_oss_date", dtObj1); messageMap.put("x_oss_credential", accessKeyId + "/" + dtObj2 + "/cn-hangzhou/oss/aliyun_v4_request"); // Replace <cn-hangzhou> with the region of your bucket. For example, for the China (Beijing) region, use cn-beijing. messageMap.put("x_oss_signature_version", "OSS4-HMAC-SHA256"); messageMap.put("policy", stringToSign); ObjectMapper objectMapper = new ObjectMapper(); // Print the signature information to be returned to the client. System.out.println(objectMapper.writeValueAsString(messageMap)); return objectMapper.writeValueAsString(messageMap); } /** * A static method to calculate the hash value for a given key and data using the HMAC-SHA256 algorithm. * @param key * @param data * @return */ public static byte[] hmacsha256(byte[] key,String data){ try { SecretKeySpec secretKeySpec = new SecretKeySpec(key, "HmacSHA256"); Mac mac = Mac.getInstance("HmacSHA256"); mac.init(secretKeySpec); byte[] hmacBytes = mac.doFinal(data.getBytes()); return hmacBytes; }catch (Exception e){ throw new RuntimeException("Failed to calculate HMAC-SHA256", e); } } }
Python
Use the following example to calculate the V4 signature on the Python server side.
Configure dependencies.
sudo pip install oss2 sudo pip install alibabacloud_credentialsSample code.
from flask import Flask, jsonify import base64 import hmac import hashlib import os import datetime import json import time from alibabacloud_credentials.client import Client as CredClient from alibabacloud_credentials.models import Config as CredConfig app = Flask(__name__) def hmacsha256(key, data): """ A function to calculate the HMAC-SHA-256 hash value. :param key: The key used to calculate the hash, in bytes. :param data: The data to be hashed, as a string. :return: The calculated HMAC-SHA-256 hash value, in bytes. """ try: mac = hmac.new(key, data.encode(), hashlib.sha256) hmacBytes = mac.digest() return hmacBytes except Exception as e: raise RuntimeError(f"Failed to calculate HMAC-SHA256 due to {e}") @app.route('/generate_signature', methods=['GET']) def generate_signature(): """ Handles requests to generate signature information, executes the relevant logic flow including obtaining environment variables, creating a policy, constructing the string-to-sign, and calculating the signature. Then, it returns the generated signature information. :return: A JSON-formatted response containing a dictionary of signature information, in the following format: { "policy": "policy_string", "x-oss-signature-version": "OSS4-HMAC-SHA256", "x-oss-credential": "accesskeyid/date/cn-hangzhou/oss/aliyun_v4_request", "x-oss-date": "request_time", "signature": "signature", "security_token": "security_token" } """ credentialConfig = CredConfig( # This is a static field. Do not change it. type='ecs_ram_role', # Replace with the name of the RAM role attached to the ECS instance in step a. role_name='role_name' ) credentialsClient = CredClient(credentialConfig) credential = credentialsClient.get_credential() # Get the AccessKey ID. accesskeyid = credential.access_key_id # Get the AccessKey secret. accesskeysecret = credential.access_key_secret # Get the token. security_token = credential.security_token now = int(time.time()) # Convert the timestamp to a datetime object. dt_obj = datetime.datetime.utcfromtimestamp(now) # Add 3 hours to the current time to set the request expiration time. dt_obj_plus_3h = dt_obj + datetime.timedelta(hours=3) # Request time. dt_obj_1 = dt_obj.strftime('%Y%m%dT%H%M%S') + 'Z' # Request date. dt_obj_2 = dt_obj.strftime('%Y%m%d') # Request expiration time. expiration_time = dt_obj_plus_3h.strftime('%Y-%m-%dT%H:%M:%S.000Z') # Step 1: Create the policy. # The example policy form fields list only the required fields. For more information about other fields, see https://help.aliyun.com/zh/oss/developer-reference/signature-version-4-recommend. policy = { "expiration": expiration_time, "conditions": [ {"bucket": "bucket_name"}, # Replace <bucket_name> with your actual bucket name. {"x-oss-signature-version": "OSS4-HMAC-SHA256"}, {"x-oss-credential": f"{accesskeyid}/{dt_obj_2}/cn-hangzhou/oss/aliyun_v4_request"}, # Replace <cn-hangzhou> with the region of your bucket. For example, for the China (Beijing) region, use cn-beijing. {"x-oss-security-token": security_token}, {"x-oss-date": dt_obj_1}, ] } policy_str = json.dumps(policy).strip() # Step 2: Construct the string-to-sign. stringToSign = base64.b64encode(policy_str.encode()).decode() # Step 3: Calculate the signing key. dateKey = hmacsha256(("aliyun_v4" + accesskeysecret).encode(), dt_obj_2) dateRegionKey = hmacsha256(dateKey, "cn-hangzhou") # Replace <cn-hangzhou> with the region of your bucket. For example, for the China (Beijing) region, use cn-beijing. dateRegionServiceKey = hmacsha256(dateRegionKey, "oss") signingKey = hmacsha256(dateRegionServiceKey, "aliyun_v4_request") # Step 4: Calculate the signature. result = hmacsha256(signingKey, stringToSign) signature = result.hex() return jsonify({ "policy": stringToSign, # Form field. "x_oss_signature_version": "OSS4-HMAC-SHA256", # Specifies the signature version and algorithm. The value is fixed to OSS4-HMAC-SHA256. "x_oss_credential": f"{accesskeyid}/{dt_obj_2}/cn-hangzhou/oss/aliyun_v4_request", # Specifies the parameter set for deriving the key. Replace <cn-hangzhou> with the region of your bucket. For example, for the China (Beijing) region, use cn-beijing. "x_oss_date": dt_obj_1, # The request time. "signature": signature, # The signature authentication description. "security_token": security_token # The security token. }) if __name__ == "__main__": app.run(host='127.0.0.1', port=5000) # To listen on other addresses such as 0.0.0.0, you must add an authentication mechanism on the server side.
Node.js
Use the following example to calculate the V4 signature on the Node.js server side.
Configure dependencies.
npm install ali-oss npm install @alicloud/credentials npm install expressSample code.
const express = require('express'); const OSS = require('ali-oss'); const Credential = require('@alicloud/credentials'); const { getCredential } = require('ali-oss/lib/common/signUtils'); const { getStandardRegion } = require('ali-oss/lib/common/utils/getStandardRegion'); const { policy2Str } = require('ali-oss/lib/common/utils/policy2Str'); const app = express(); const PORT = process.env.PORT || 5000; // The service request port. const ECSGenerateSignature = async () => { // Initialize the ECS RAM role credentials. const credentialsConfig = new Credential.Config({ type: 'ecs_ram_role', // This is a static field. Do not change it. roleName: 'role_name', // Replace with the name of the role assumed by the ECS instance. }); const credentialClient = new Credential.default(credentialsConfig); const { accessKeyId, accessKeySecret, securityToken } = await credentialClient.getCredential(); // Initialize the OSS client. const client = new OSS({ bucket: 'bucketname', // Replace with the destination bucket name. region: 'cn-hangzhou', // Replace with the region of the destination bucket. accessKeyId, accessKeySecret, stsToken: securityToken, refreshSTSTokenInterval: 0, refreshSTSToken: async () => { const { accessKeyId, accessKeySecret, securityToken } = await credentialClient.getCredential(); return { accessKeyId, accessKeySecret, stsToken: securityToken }; }, }); // Create a form data map. const formData = new Map(); // Set the signature expiration time to 10 minutes from the current time. const date = new Date(); const expirationDate = new Date(date); expirationDate.setMinutes(date.getMinutes() + 10); // Format the date into a UTC time string that conforms to the ISO 8601 standard. function padTo2Digits(num) { return num.toString().padStart(2, '0'); } function formatDateToUTC(date) { return ( date.getUTCFullYear() + padTo2Digits(date.getUTCMonth() + 1) + padTo2Digits(date.getUTCDate()) + 'T' + padTo2Digits(date.getUTCHours()) + padTo2Digits(date.getUTCMinutes()) + padTo2Digits(date.getUTCSeconds()) + 'Z' ); } const formattedDate = formatDateToUTC(expirationDate); // Generate x-oss-credential and set the form data. const credential = getCredential(formattedDate.split('T')[0], getStandardRegion(client.options.region), client.options.accessKeyId); formData.set('x_oss_date', formattedDate); formData.set('x_oss_credential', credential); formData.set('x_oss_signature_version', 'OSS4-HMAC-SHA256'); // Create the policy. // The example policy form fields list only the required fields. For more information about other fields, see Signature Version 4 documentation: https://help.aliyun.com/zh/oss/developer-reference/signature-version-4-recommend. const policy = { expiration: expirationDate.toISOString(), conditions: [ { 'bucket':"bucketname"}, // Replace "bucketname" with the destination bucket name. { 'x-oss-credential': credential }, { 'x-oss-signature-version': 'OSS4-HMAC-SHA256' }, { 'x-oss-date': formattedDate }, ], }; // If an STS token exists, add it to the policy and form data. if (client.options.stsToken) { policy.conditions.push({ 'x-oss-security-token': client.options.stsToken }); formData.set('security_token', client.options.stsToken); } // Generate the signature and set the form data. const signature = client.signPostObjectPolicyV4(policy, date); formData.set('policy', Buffer.from(policy2Str(policy), 'utf8').toString('base64')); formData.set('signature', signature); // Return the form data. return Object.fromEntries(formData); }; app.get('/generate_signature', async (req, res) => { try { const result = await ECSGenerateSignature(); res.json(result); // Return the generated signature data. } catch (error) { console.error('Error generating signature:', error); res.status(500).send('Error generating signature'); } }); app.listen(PORT, () => { console.log(`Server is running on http://localhost:${PORT}`); });
Go
Use the following example to calculate the V4 signature on the Go server side.
Configure dependencies.
go get -u github.com/aliyun/credentials-go go mod tidySample code.
package main import ( "crypto/hmac" "crypto/sha256" "encoding/base64" "encoding/hex" "encoding/json" "fmt" "github.com/aliyun/credentials-go/credentials" "hash" "io" "log" "net/http" "os" "time" ) // Define global variables. var ( region string bucketName string product = "oss" ) // The PolicyToken struct is used to store the generated form data. type PolicyToken struct { Policy string `json:"policy"` SecurityToken string `json:"security_token"` SignatureVersion string `json:"x_oss_signature_version"` Credential string `json:"x_oss_credential"` Date string `json:"x_oss_date"` Signature string `json:"signature"` } func main() { // Define the default IP and port string. strIPPort := ":5000" if len(os.Args) == 3 { strIPPort = fmt.Sprintf("%s:%s", os.Args[1], os.Args[2]) } else if len(os.Args) != 1 { fmt.Println("Usage : go run callbackserver.go ") fmt.Println("Usage : go run callbackserver.go ip port ") fmt.Println("Example : go run callbackserver.go 11.22.**.** 80 ") fmt.Println("Example : go run callbackserver.go 127.0.0.1 5000 ") // To listen on other addresses such as 0.0.0.0, you must add an authentication mechanism on the server side. fmt.Println("") os.Exit(0) } // Print the address and port on which the server is running. fmt.Printf("server is running on %s \n", strIPPort) // Register the function to handle requests to the root path. http.HandleFunc("/", handlerRequest) // Start the HTTP server. err := http.ListenAndServe(strIPPort, nil) if err != nil { strError := fmt.Sprintf("http.ListenAndServe failed : %s \n", err.Error()) panic(strError) } } // The handlerRequest function handles HTTP requests. func handlerRequest(w http.ResponseWriter, r *http.Request) { if r.Method == "GET" { response := get_policy_token() w.Header().Set("Access-Control-Allow-Methods", "POST") // Set the allowed origin to all (this poses a security risk and should be used with caution in a production environment). w.Header().Set("Access-Control-Allow-Origin", "*") io.WriteString(w, response) } } func get_policy_token() string { // Set the region of the bucket. region = "cn-hangzhou" // Set the bucket name. bucketName = "bucketName" // Create an Alibaba Cloud credential configuration. config := new(credentials.Config). // Specify the credential type. The value is fixed to ecs_ram_role. SetType("ecs_ram_role"). // Specify the name of the RAM role assumed by the ECS instance. SetRoleName("roleName"). // Optional parameter. Disables IMDSv1. We recommend that you enable this parameter. SetDisableIMDSv1(true) // Create a credential provider based on the configuration. provider, err := credentials.NewCredential(config) if err != nil { log.Fatalf("NewCredential fail, err:%v", err) } // Obtain credentials from the credential provider. cred, err := provider.GetCredential() if err != nil { log.Fatalf("GetCredential fail, err:%v", err) } // Build the policy. utcTime := time.Now().UTC() date := utcTime.Format("20060102") // Set the signature to expire one hour from the current time. expiration := utcTime.Add(1 * time.Hour) // The example policy form fields list only some of the required fields. For more information about other fields, see Signature Version 4 documentation: https://help.aliyun.com/zh/oss/developer-reference/signature-version-4-recommend. policyMap := map[string]any{ "expiration": expiration.Format("2006-01-02T15:04:05.000Z"), "conditions": []any{ map[string]string{"bucket": bucketName}, map[string]string{"x-oss-signature-version": "OSS4-HMAC-SHA256"}, map[string]string{"x-oss-credential": fmt.Sprintf("%v/%v/%v/%v/aliyun_v4_request",*cred.AccessKeyId, date, region, product)}, map[string]string{"x-oss-date": utcTime.Format("20060102T150405Z")}, map[string]string{"x-oss-security-token": *cred.SecurityToken}, }, } // Convert the policy to the JSON format. policy, err := json.Marshal(policyMap) if err != nil { log.Fatalf("json.Marshal fail, err:%v", err) } // Construct the string-to-sign. stringToSign := base64.StdEncoding.EncodeToString([]byte(policy)) hmacHash := func() hash.Hash { return sha256.New() } // Build the signing key. signingKey := "aliyun_v4" + *cred.AccessKeySecret h1 := hmac.New(hmacHash, []byte(signingKey)) io.WriteString(h1, date) h1Key := h1.Sum(nil) h2 := hmac.New(hmacHash, h1Key) io.WriteString(h2, region) h2Key := h2.Sum(nil) h3 := hmac.New(hmacHash, h2Key) io.WriteString(h3, product) h3Key := h3.Sum(nil) h4 := hmac.New(hmacHash, h3Key) io.WriteString(h4, "aliyun_v4_request") h4Key := h4.Sum(nil) // Generate the signature. h := hmac.New(hmacHash, h4Key) io.WriteString(h, stringToSign) signature := hex.EncodeToString(h.Sum(nil)) // Build the form to be returned to the frontend. policyToken := PolicyToken{ Policy: stringToSign, Credential: fmt.Sprintf("%v/%v/%v/%v/aliyun_v4_request",*cred.AccessKeyId, date, region, product), SignatureVersion: "OSS4-HMAC-SHA256", Signature: signature, Date: utcTime.UTC().Format("20060102T150405Z"), SecurityToken: *cred.SecurityToken, } response, err := json.Marshal(policyToken) if err != nil { fmt.Println("json err:", err) } fmt.Println(string(response)) // Return the form. return string(response) }
PHP
Use the following example to calculate the V4 signature on the PHP server side.
Configure dependencies.
composer require alibabacloud/credentialsSample code.
<?php require_once 'vendor/autoload.php'; use AlibabaCloud\Credentials\Credential; // Set the region of the bucket. $region = 'cn-hangzhou'; // Set the bucket name. $bucket = 'bucketName'; $product = 'oss'; // Create an Alibaba Cloud credential configuration. $config = new Credential\Config([ // Specify the credential type. The value is fixed to ecs_ram_role. 'type' => 'ecs_ram_role', // Set the name of the RAM role for the ECS instance. 'roleName' => "roleName", ]); // Create a credential object based on the configuration. $credential = new Credential($config); // Obtain credential information from the credential object. $cred = $credential->getCredential(); // Get the current UTC time. $utcTime = new DateTime('now', new DateTimeZone('UTC')); // Format the current date to Ymd, for example, 20240101. $date = $utcTime->format('Ymd'); // Clone the current time object to set the expiration time. $expiration = clone $utcTime; // Set the expiration time to 1 hour from the current time. $expiration->add(new DateInterval('PT1H')); // Build the policy. // The example policy form fields list only some of the required fields. For more information about other fields, see Signature Version 4 documentation: https://help.aliyun.com/zh/oss/developer-reference/signature-version-4-recommend. $policyMap = [ "expiration" => $expiration->format('Y-m-d\TH:i:s.000\Z'), "conditions" => [ ["bucket" => $bucket], ["x-oss-signature-version" => "OSS4-HMAC-SHA256"], ["x-oss-credential" => sprintf("%s/%s/%s/%s/aliyun_v4_request",$cred->getAccessKeyId(), $date, $region, $product)], ["x-oss-date" => $utcTime->format('Ymd\THis\Z')], ["x-oss-security-token" => $cred->getSecurityToken()], ], ]; // Convert the policy to a JSON-formatted string. $policy = json_encode($policyMap); // Base64-encode the policy string to get the string-to-sign. $stringToSign = base64_encode($policy); // Build the signing key by concatenating the fixed string "aliyun_v4" and the AccessKey secret. $signingKey = "aliyun_v4" . $cred->getAccessKeySecret(); $h1Key = hmacSign($signingKey, $date); $h2Key = hmacSign($h1Key, $region); $h3Key = hmacSign($h2Key, $product); $h4Key = hmacSign($h3Key, "aliyun_v4_request"); // Use h4Key to perform HMAC-SHA256 signing on the string-to-sign to get the final signature. $signature = hash_hmac('sha256', $stringToSign, $h4Key); // Build the form data to be returned to the frontend, including the policy, signature version, credential, date, signature, and security token. echo json_encode(array( 'policy' => $stringToSign, "x_oss_signature_version" => "OSS4-HMAC-SHA256", "x_oss_credential" => sprintf("%s/%s/%s/%s/aliyun_v4_request", $cred->getAccessKeyId(), $date, $region, $product), "x_oss_date" => $utcTime->format('Ymd\THis\Z'), "signature" => $signature, "security_token" => $cred->getSecurityToken() )); // The hmacSign function, used for HMAC-SHA256 signature calculation. function hmacSign($key, $data) { return hash_hmac('sha256', $data, $key, true); }
Obtain an STS temporary access credential and calculate a signature as a RAM user
Grant permissions to the RAM user. For more information about how to create a RAM user, see Create a RAM user.
Grant permissions to the RAM role. For more information about how to create a RAM role, see Create a RAM role for a trusted Alibaba Cloud account.
Calculate the signature on the server side.
Java
Use the following example to calculate the V4 signature on the Java server side. For the complete sample project, deploy upload_server.zip. Note that this sample project is built with JDK 23 and Spring Boot 3.4.0. You may need to adjust it based on your environment to ensure that the code runs as expected.
Configure environment variables.
NoteReplace
<ALIBABA_CLOUD_ACCESS_KEY_ID>and<ALIBABA_CLOUD_ACCESS_KEY_SECRET>with the AccessKey ID and AccessKey secret of a RAM user, respectively. For more information about how to create an AccessKey ID and an AccessKey secret, see Create an AccessKey.Replace
<ROLE_ARN>with the ARN of the target role. On the Roles page, click the name of the target RAM role and find its ARN in the Basic Information section.
macOS/Linux/Unix systems.
export OSS_ACCESS_KEY_ID=<ALIBABA_CLOUD_ACCESS_KEY_ID> export OSS_ACCESS_KEY_SECRET=<ALIBABA_CLOUD_ACCESS_KEY_SECRET> export OSS_STS_ROLE_ARN=<ROLE_ARN>Windows systems.
set OSS_ACCESS_KEY_ID=<ALIBABA_CLOUD_ACCESS_KEY_ID> set OSS_ACCESS_KEY_SECRET=<ALIBABA_CLOUD_ACCESS_KEY_SECRET> set OSS_STS_ROLE_ARN=<ROLE_ARN>
Configure dependencies.
<dependency> <groupId>com.aliyun.oss</groupId> <artifactId>aliyun-sdk-oss</artifactId> <version>3.17.4</version> </dependency>Sample API operation.
package com.example.demo.controller; import com.example.demo.util.ECSGenerateSignature; import com.example.demo.util.RAMGenerateSignature; import com.fasterxml.jackson.core.JsonProcessingException; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class VxController { /** * Obtain temporary access credentials using the AccessKey pair in the environment variables, calculate the signature information, and return it to the mini program client. * @return * @throws JsonProcessingException */ @GetMapping("/generate_signature") public String generate_signature() throws JsonProcessingException { RAMGenerateSignature ramGenerateSignature = new RAMGenerateSignature(); return ramGenerateSignature.getSignature(); } }Sample signature information utility class.
package com.example.demo.util; import com.aliyun.oss.common.utils.BinaryUtil; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.commons.codec.binary.Base64; import com.aliyuncs.DefaultAcsClient; import com.aliyuncs.IAcsClient; import com.aliyuncs.auth.sts.AssumeRoleRequest; import com.aliyuncs.auth.sts.AssumeRoleResponse; import com.aliyuncs.exceptions.ClientException; import com.aliyuncs.profile.DefaultProfile; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import java.time.Instant; import java.time.ZoneId; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * Obtain a temporary access credential and calculate a signature using the AccessKey pair in the environment variables. */ public class RAMGenerateSignature { public String getSignature() throws JsonProcessingException { // Obtain the basic information for sending an STS request. String accessKeyId = System.getenv("OSS_ACCESS_KEY_ID"); // Obtain the AccessKey ID from the environment variable. String accessKeySecret = System.getenv("OSS_ACCESS_KEY_SECRET"); // Obtain the AccessKey secret from the environment variable. String roleArnForOssUpload = System.getenv("OSS_STS_ROLE_ARN"); // Obtain the ARN from the environment variable. String regionId = "cn-hangzhou"; // The region where the STS request is initiated. String roleSessionName = "<YOUR_ROLE_SESSION_NAME>"; // The role session name, used to distinguish different tokens. You can customize this. Long durationSeconds = 3600L; // The validity period of the temporary access credential. // Initialize the client. DefaultProfile profile = DefaultProfile.getProfile(regionId, accessKeyId, accessKeySecret); IAcsClient client = new DefaultAcsClient(profile); AssumeRoleRequest request = new AssumeRoleRequest(); request.setRoleArn(roleArnForOssUpload); request.setRoleSessionName(roleSessionName); request.setDurationSeconds(durationSeconds); // Define STS temporary access credential variables. String STSaccessKeyId = null; String STSsecretAccessKey = null; String securityToken = null; try { AssumeRoleResponse response = client.getAcsResponse(request); // Assign the STS temporary access credential returned from the request to custom variables. STSaccessKeyId = response.getCredentials().getAccessKeyId(); STSsecretAccessKey = response.getCredentials().getAccessKeySecret(); securityToken = response.getCredentials().getSecurityToken(); } catch (ClientException e) { e.printStackTrace(); } // Format the request date. long now = System.currentTimeMillis() / 1000; ZonedDateTime dtObj = ZonedDateTime.ofInstant(Instant.ofEpochSecond(now), ZoneId.of("UTC")); ZonedDateTime dtObjPlus3h = dtObj.plusHours(3); // Request time. DateTimeFormatter dtObj1Formatter = DateTimeFormatter.ofPattern("yyyyMMdd'T'HHmmss'Z'"); String dtObj1 = dtObj.format(dtObj1Formatter); // Request date. DateTimeFormatter dtObj2Formatter = DateTimeFormatter.ofPattern("yyyyMMdd"); String dtObj2 = dtObj.format(dtObj2Formatter); // Request expiration time. DateTimeFormatter expirationTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"); String expirationTime = dtObjPlus3h.format(expirationTimeFormatter); // Create the policy. // The example policy form fields list only the required fields. For more information about other fields, see https://help.aliyun.com/zh/oss/developer-reference/signature-version-4-recommend. ObjectMapper mapper = new ObjectMapper(); Map<String, Object> policy = new HashMap<>(); policy.put("expiration", expirationTime); List<Object> conditions = new ArrayList<>(); Map<String, String> bucketCondition = new HashMap<>(); bucketCondition.put("bucket", "bucketname"); // Replace <bucketname> with your actual bucket name. conditions.add(bucketCondition); Map<String, String> signatureVersionCondition = new HashMap<>(); signatureVersionCondition.put("x-oss-signature-version", "OSS4-HMAC-SHA256"); conditions.add(signatureVersionCondition); Map<String, String> credentialCondition = new HashMap<>(); credentialCondition.put("x-oss-credential", STSaccessKeyId + "/" + dtObj2 + "/cn-hangzhou/oss/aliyun_v4_request"); // Replace <cn-hangzhou> with the region of your bucket. For example, for the China (Beijing) region, use cn-beijing. conditions.add(credentialCondition); Map<String, String> token = new HashMap<>(); token.put("x-oss-security-token", securityToken); conditions.add(token); Map<String, String> dateCondition = new HashMap<>(); dateCondition.put("x-oss-date", dtObj1); conditions.add(dateCondition); policy.put("conditions", conditions); String jsonPolicy = mapper.writeValueAsString(policy); // Construct the string-to-sign. String stringToSign = new String(Base64.encodeBase64(jsonPolicy.getBytes())); // Calculate the signing key. byte[] dateKey = hmacsha256(("aliyun_v4" + STSsecretAccessKey).getBytes(), dtObj2); byte[] dateRegionKey = hmacsha256(dateKey, "cn-hangzhou"); // Replace <cn-hangzhou> with the region of your bucket. For example, for the China (Beijing) region, use cn-beijing. byte[] dateRegionServiceKey = hmacsha256(dateRegionKey, "oss"); byte[] signingKey = hmacsha256(dateRegionServiceKey, "aliyun_v4_request"); // Calculate the signature. byte[] result = hmacsha256(signingKey, stringToSign); String signature = BinaryUtil.toHex(result); Map<String, String> messageMap = new HashMap<>(); messageMap.put("security_token", securityToken); messageMap.put("signature", signature); messageMap.put("x_oss_date", dtObj1); messageMap.put("x_oss_credential", STSaccessKeyId + "/" + dtObj2 + "/cn-hangzhou/oss/aliyun_v4_request"); // Replace <cn-hangzhou> with the region of your bucket. For example, for the China (Beijing) region, use cn-beijing. messageMap.put("x_oss_signature_version", "OSS4-HMAC-SHA256"); messageMap.put("policy", stringToSign); ObjectMapper objectMapper = new ObjectMapper(); // Print the signature information to be returned to the client. // System.out.println(objectMapper.writeValueAsString(messageMap)); return objectMapper.writeValueAsString(messageMap); } /** * A static method to calculate the hash value for a given key and data using the HMAC-SHA256 algorithm. * @param key * @param data * @return */ public static byte[] hmacsha256(byte[] key,String data){ try { SecretKeySpec secretKeySpec = new SecretKeySpec(key, "HmacSHA256"); Mac mac = Mac.getInstance("HmacSHA256"); mac.init(secretKeySpec); byte[] hmacBytes = mac.doFinal(data.getBytes()); return hmacBytes; }catch (Exception e){ throw new RuntimeException("Failed to calculate HMAC-SHA256", e); } } }
Python
Use the following example to calculate the V4 signature on the Python server side.
Configure environment variables.
NoteReplace
<ALIBABA_CLOUD_ACCESS_KEY_ID>and<ALIBABA_CLOUD_ACCESS_KEY_SECRET>with the AccessKey ID and AccessKey secret of a RAM user, respectively. For more information about how to create an AccessKey ID and an AccessKey secret, see Create an AccessKey.Replace
<ROLE_ARN>with the ARN of the target role. On the Roles page, click the name of the target RAM role and find its ARN in the Basic Information section.
macOS/Linux/Unix
export OSS_ACCESS_KEY_ID=<ALIBABA_CLOUD_ACCESS_KEY_ID> export OSS_ACCESS_KEY_SECRET=<ALIBABA_CLOUD_ACCESS_KEY_SECRET> export OSS_STS_ROLE_ARN=<ROLE_ARN>Windows
set OSS_ACCESS_KEY_ID=<ALIBABA_CLOUD_ACCESS_KEY_ID> set OSS_ACCESS_KEY_SECRET=<ALIBABA_CLOUD_ACCESS_KEY_SECRET> set OSS_STS_ROLE_ARN=<ROLE_ARN>
Configure dependencies.
pip install oss2 pip install alibabacloud_tea_openapi alibabacloud_sts20150401 alibabacloud_credentialsSample code.
from flask import Flask, jsonify import base64 import hmac import hashlib import os import datetime import json import time from alibabacloud_tea_openapi.models import Config from alibabacloud_sts20150401.client import Client as Sts20150401Client from alibabacloud_sts20150401 import models as sts_20150401_models from alibabacloud_credentials.client import Client as CredentialClient import os app = Flask(__name__) def hmacsha256(key, data): """ A function to calculate the HMAC-SHA-256 hash value. :param key: The key used to calculate the hash, in bytes. :param data: The data to be hashed, as a string. :return: The calculated HMAC-SHA-256 hash value, in bytes. """ try: mac = hmac.new(key, data.encode(), hashlib.sha256) hmacBytes = mac.digest() return hmacBytes except Exception as e: raise RuntimeError(f"Failed to calculate HMAC-SHA256 due to {e}") @app.route('/generate_signature', methods=['GET']) def generate_signature(): """ Handles requests to generate signature information, executes the relevant logic flow including obtaining environment variables, creating a policy, constructing the string-to-sign, and calculating the signature. Then, it returns the generated signature information. :return: A JSON-formatted response containing a dictionary of signature information, in the following format: { "policy": "policy_string", "x-oss-signature-version": "OSS4-HMAC-SHA256", "x-oss-credential": "accesskeyid/date/cn-hangzhou/oss/aliyun_v4_request", "x-oss-date": "request_time", "signature": "signature", "security_token": "security_token" } """ access_key_id = os.environ.get('OSS_ACCESS_KEY_ID') access_key_secret = os.environ.get('OSS_ACCESS_KEY_SECRET') role_arn_for_oss_upload = os.environ.get('OSS_STS_ROLE_ARN') # Custom session name. role_session_name = 'role_session_name' # Specify the expiration time in seconds. expire_time = 3600 bucket = 'examplebucket' region_id = 'cn-hangzhou' # Initialize the configuration and pass the credentials directly. config = Config( region_id=region_id, access_key_id=access_key_id, access_key_secret=access_key_secret ) # Create an STS client and obtain temporary credentials. sts_client = Sts20150401Client(config=config) assume_role_request = sts_20150401_models.AssumeRoleRequest( role_arn=role_arn_for_oss_upload, role_session_name=role_session_name ) response = sts_client.assume_role(assume_role_request) token_data = response.body.credentials.to_map() # Use the temporary credentials returned by STS. sts_accesskeyid = token_data['AccessKeyId'] sts_accesskeysecret = token_data['AccessKeySecret'] security_token = token_data['SecurityToken'] now = int(time.time()) # Convert the timestamp to a datetime object. dt_obj = datetime.datetime.utcfromtimestamp(now) # Add 3 hours to the current time to set the request expiration time. dt_obj_plus_3h = dt_obj + datetime.timedelta(hours=3) # Request time. dt_obj_1 = dt_obj.strftime('%Y%m%dT%H%M%S') + 'Z' # Request date. dt_obj_2 = dt_obj.strftime('%Y%m%d') # Request expiration time. expiration_time = dt_obj_plus_3h.strftime('%Y-%m-%dT%H:%M:%S.000Z') # Step 1: Create the policy. # The example policy form fields list only the required fields. For more information about other fields, see Signature Version 4 documentation: https://help.aliyun.com/zh/oss/developer-reference/signature-version-4-recommend. policy = { "expiration": expiration_time, "conditions": [ {"bucket": "bucket_name"}, # Replace <bucket_name> with your actual bucket name. {"x-oss-signature-version": "OSS4-HMAC-SHA256"}, {"x-oss-credential": f"{sts_accesskeyid}/{dt_obj_2}/cn-hangzhou/oss/aliyun_v4_request"}, # Replace <cn-hangzhou> with the region of your bucket. For example, for the China (Beijing) region, use cn-beijing. {"x-oss-security-token": security_token}, {"x-oss-date": dt_obj_1}, ] } policy_str = json.dumps(policy).strip() # Step 2: Construct the string-to-sign. stringToSign = base64.b64encode(policy_str.encode()).decode() # Step 3: Calculate the signing key. dateKey = hmacsha256(("aliyun_v4" + sts_accesskeysecret).encode(), dt_obj_2) dateRegionKey = hmacsha256(dateKey, "cn-hangzhou") # Replace <cn-hangzhou> with the region of your bucket. For example, for the China (Beijing) region, use cn-beijing. dateRegionServiceKey = hmacsha256(dateRegionKey, "oss") signingKey = hmacsha256(dateRegionServiceKey, "aliyun_v4_request") # Step 4: Calculate the signature. result = hmacsha256(signingKey, stringToSign) signature = result.hex() return jsonify({ "policy": stringToSign, "x_oss_signature_version": "OSS4-HMAC-SHA256", "x_oss_credential": f"{sts_accesskeyid}/{dt_obj_2}/cn-hangzhou/oss/aliyun_v4_request", # Replace <cn-hangzhou> with the region of your bucket. For example, for the China (Beijing) region, use cn-beijing. "x_oss_date": dt_obj_1, "signature": signature, "security_token": security_token }) if __name__ == "__main__": app.run(host='127.0.0.1', port=5000) # To listen on other addresses such as 0.0.0.0, you must add an authentication mechanism on the server side.
Node.js
Use the following example to calculate the V4 signature on the Node.js server side.
Configure environment variables.
NoteReplace
<ALIBABA_CLOUD_ACCESS_KEY_ID>and<ALIBABA_CLOUD_ACCESS_KEY_SECRET>with the AccessKey ID and AccessKey secret of a RAM user, respectively. For more information about how to create an AccessKey ID and an AccessKey secret, see Create an AccessKey.Replace
<ROLE_ARN>with the ARN of the target role. On the Roles page, click the name of the target RAM role and find its ARN in the Basic Information section.
macOS/Linux/Unix
export OSS_ACCESS_KEY_ID=<ALIBABA_CLOUD_ACCESS_KEY_ID> export OSS_ACCESS_KEY_SECRET=<ALIBABA_CLOUD_ACCESS_KEY_SECRET> export OSS_STS_ROLE_ARN=<ROLE_ARN>Windows
set OSS_ACCESS_KEY_ID=<ALIBABA_CLOUD_ACCESS_KEY_ID> set OSS_ACCESS_KEY_SECRET=<ALIBABA_CLOUD_ACCESS_KEY_SECRET> set OSS_STS_ROLE_ARN=<ROLE_ARN>
Configure dependencies.
npm install ali-oss npm install @alicloud/credentials npm install expressSample code.
const express = require('express'); const OSS = require('ali-oss'); const { STS } = require('ali-oss'); const Credential = require("@alicloud/credentials"); const { getCredential } = require('ali-oss/lib/common/signUtils'); const { getStandardRegion } = require('ali-oss/lib/common/utils/getStandardRegion'); const { policy2Str } = require('ali-oss/lib/common/utils/policy2Str'); const app = express(); const PORT = process.env.PORT || 5000; // The service request port. const ECSGenerateSignature = async () => { // Initialize the STS client. let sts = new STS({ accessKeyId : process.env.OSS_ACCESS_KEY_ID, // Obtain the AccessKey ID of the RAM user from the environment variable. accessKeySecret : process.env.OSS_ACCESS_KEY_SECRET // Obtain the AccessKey secret of the RAM user from the environment variable. }); // Call the assumeRole operation to obtain an STS temporary access credential. const result = await sts.assumeRole(process.env.OSS_STS_ROLE_ARN, '', '3600', 'sessiontest'); // Obtain the RAM role ARN from the environment variable, set the validity period of the temporary access credential to 3600 seconds, and the role session name to sessiontest (customizable). // Extract the AccessKeyId, AccessKeySecret, and SecurityToken from the temporary access credential. const accessKeyId = result.credentials.AccessKeyId; const accessKeySecret = result.credentials.AccessKeySecret; const securityToken = result.credentials.SecurityToken; // Initialize the OSS Client. const client = new OSS({ bucket: 'bucketname', // Replace with the destination bucket name. region: 'cn-hangzhou', // Replace with the region of the destination bucket. accessKeyId, accessKeySecret, stsToken: securityToken, refreshSTSTokenInterval: 0, refreshSTSToken: async () => { const { accessKeyId, accessKeySecret, securityToken } = await credentialClient.getCredential(); return { accessKeyId, accessKeySecret, stsToken: securityToken }; }, }); // Create a form data map. const formData = new Map(); // Set the signature expiration time to 10 minutes from the current time. const date = new Date(); const expirationDate = new Date(date); expirationDate.setMinutes(date.getMinutes() + 10); // Format the date into a UTC time string that conforms to the ISO 8601 standard. function padTo2Digits(num) { return num.toString().padStart(2, '0'); } function formatDateToUTC(date) { return ( date.getUTCFullYear() + padTo2Digits(date.getUTCMonth() + 1) + padTo2Digits(date.getUTCDate()) + 'T' + padTo2Digits(date.getUTCHours()) + padTo2Digits(date.getUTCMinutes()) + padTo2Digits(date.getUTCSeconds()) + 'Z' ); } const formattedDate = formatDateToUTC(expirationDate); // Generate x-oss-credential and set the form data. const credential = getCredential(formattedDate.split('T')[0], getStandardRegion(client.options.region), client.options.accessKeyId); formData.set('x_oss_date', formattedDate); formData.set('x_oss_credential', credential); formData.set('x_oss_signature_version', 'OSS4-HMAC-SHA256'); // Create the policy. // The example policy form fields list only the required fields. For more information about other fields, see https://help.aliyun.com/zh/oss/developer-reference/signature-version-4-recommend. const policy = { expiration: expirationDate.toISOString(), conditions: [ { 'bucket':"bucketname"}, // Replace "bucketname" with the destination bucket name. { 'x-oss-credential': credential }, { 'x-oss-signature-version': 'OSS4-HMAC-SHA256' }, { 'x-oss-date': formattedDate }, ], }; // If an STS token exists, add it to the policy and form data. if (client.options.stsToken) { policy.conditions.push({ 'x-oss-security-token': client.options.stsToken }); formData.set('security_token', client.options.stsToken); } // Generate the signature and set the form data. const signature = client.signPostObjectPolicyV4(policy, date); formData.set('policy', Buffer.from(policy2Str(policy), 'utf8').toString('base64')); formData.set('signature', signature); // Return the form data. return Object.fromEntries(formData); }; app.get('/generate_signature', async (req, res) => { try { const result = await ECSGenerateSignature(); res.json(result); // Return the generated signature data. } catch (error) { console.error('Error generating signature:', error); res.status(500).send('Error generating signature'); } }); app.listen(PORT, () => { console.log(`Server is running on http://localhost:${PORT}`); });
Go
Use the following example to calculate the V4 signature on the Go server side.
Configure environment variables.
NoteReplace
<ALIBABA_CLOUD_ACCESS_KEY_ID>and<ALIBABA_CLOUD_ACCESS_KEY_SECRET>with the AccessKey ID and AccessKey secret of a RAM user, respectively. For more information about how to create an AccessKey ID and an AccessKey secret, see Create an AccessKey.Replace
<ROLE_ARN>with the ARN of the target role. On the Roles page, click the name of the target RAM role and find its ARN in the Basic Information section.
macOS/Linux/Unix
export OSS_ACCESS_KEY_ID=<ALIBABA_CLOUD_ACCESS_KEY_ID> export OSS_ACCESS_KEY_SECRET=<ALIBABA_CLOUD_ACCESS_KEY_SECRET> export OSS_STS_ROLE_ARN=<ROLE_ARN>Windows
set OSS_ACCESS_KEY_ID=<ALIBABA_CLOUD_ACCESS_KEY_ID> set OSS_ACCESS_KEY_SECRET=<ALIBABA_CLOUD_ACCESS_KEY_SECRET> set OSS_STS_ROLE_ARN=<ROLE_ARN>
Configure dependencies.
go get -u github.com/aliyun/credentials-go go mod tidySample code.
package main import ( "crypto/hmac" "crypto/sha256" "encoding/base64" "encoding/hex" "encoding/json" "fmt" "github.com/aliyun/credentials-go/credentials" "hash" "io" "log" "net/http" "os" "time" ) // Define global variables. var ( region string bucketName string product = "oss" ) // The PolicyToken struct is used to store the generated form data. type PolicyToken struct { Policy string `json:"policy"` SecurityToken string `json:"security_token"` SignatureVersion string `json:"x_oss_signature_version"` Credential string `json:"x_oss_credential"` Date string `json:"x_oss_date"` Signature string `json:"signature"` } func main() { // Define the default IP and port string. strIPPort := ":8080" if len(os.Args) == 3 { strIPPort = fmt.Sprintf("%s:%s", os.Args[1], os.Args[2]) } else if len(os.Args) != 1 { fmt.Println("Usage : go run callbackserver.go ") fmt.Println("Usage : go run callbackserver.go ip port ") fmt.Println("Example : go run callbackserver.go 11.22.**.** 80 ") fmt.Println("Example : go run callbackserver.go 127.0.0.1 8080 ") // To listen on other addresses such as 0.0.0.0, you must add an authentication mechanism on the server side. fmt.Println("") os.Exit(0) } // Print the address and port on which the server is running. fmt.Printf("server is running on %s \n", strIPPort) // Register the function to handle requests to the root path. http.HandleFunc("/", handlerRequest) // Start the HTTP server. err := http.ListenAndServe(strIPPort, nil) if err != nil { strError := fmt.Sprintf("http.ListenAndServe failed : %s \n", err.Error()) panic(strError) } } // The handlerRequest function handles HTTP requests. func handlerRequest(w http.ResponseWriter, r *http.Request) { if r.Method == "GET" { response := get_policy_token() w.Header().Set("Access-Control-Allow-Methods", "POST") // Set the allowed origin to all (this poses a security risk and should be used with caution in a production environment). w.Header().Set("Access-Control-Allow-Origin", "*") io.WriteString(w, response) } } func get_policy_token() string { // Set the region of the bucket. region = "cn-hangzhou" // Set the bucket name. bucketName = "bucketName" config := new(credentials.Config). // Specify the credential type. The value is fixed to ram_role_arn. SetType("ram_role_arn"). // Obtain the AccessKey pair (AccessKeyId and AccessKeySecret) of the RAM user from the environment variables. SetAccessKeyId(os.Getenv("OSS_ACCESS_KEY_ID")). SetAccessKeySecret(os.Getenv("OSS_ACCESS_KEY_SECRET")). // Obtain the ARN of the RAM role from the environment variable. This is the ID of the role to be assumed. The format is acs:ram::$accountID:role/$roleName. SetRoleArn(os.Getenv("OSS_STS_ROLE_ARN")). // Customize the role session name to distinguish different tokens. SetRoleSessionName("Role_Session_Name"). // (Optional) Restrict the permissions of the STS token. SetPolicy(""). // (Optional) Limit the validity period of the STS token. SetRoleSessionExpiration(3600) // Create a credential provider based on the configuration. provider, err := credentials.NewCredential(config) if err != nil { log.Fatalf("NewCredential fail, err:%v", err) } // Obtain credentials from the credential provider. cred, err := provider.GetCredential() if err != nil { log.Fatalf("GetCredential fail, err:%v", err) } // Build the policy. utcTime := time.Now().UTC() date := utcTime.Format("20060102") // Set the signature to expire one hour from the current time. expiration := utcTime.Add(1 * time.Hour) // The example policy form fields list only some of the required fields. For more information about other fields, see Signature Version 4 documentation: https://help.aliyun.com/zh/oss/developer-reference/signature-version-4-recommend. policyMap := map[string]any{ "expiration": expiration.Format("2006-01-02T15:04:05.000Z"), "conditions": []any{ map[string]string{"bucket": bucketName}, map[string]string{"x-oss-signature-version": "OSS4-HMAC-SHA256"}, map[string]string{"x-oss-credential": fmt.Sprintf("%v/%v/%v/%v/aliyun_v4_request",*cred.AccessKeyId, date, region, product)}, map[string]string{"x-oss-date": utcTime.Format("20060102T150405Z")}, map[string]string{"x-oss-security-token": *cred.SecurityToken}, }, } // Convert the policy to the JSON format. policy, err := json.Marshal(policyMap) if err != nil { log.Fatalf("json.Marshal fail, err:%v", err) } // Construct the string-to-sign. stringToSign := base64.StdEncoding.EncodeToString([]byte(policy)) hmacHash := func() hash.Hash { return sha256.New() } // Build the signing key. signingKey := "aliyun_v4" + *cred.AccessKeySecret h1 := hmac.New(hmacHash, []byte(signingKey)) io.WriteString(h1, date) h1Key := h1.Sum(nil) h2 := hmac.New(hmacHash, h1Key) io.WriteString(h2, region) h2Key := h2.Sum(nil) h3 := hmac.New(hmacHash, h2Key) io.WriteString(h3, product) h3Key := h3.Sum(nil) h4 := hmac.New(hmacHash, h3Key) io.WriteString(h4, "aliyun_v4_request") h4Key := h4.Sum(nil) // Generate the signature. h := hmac.New(hmacHash, h4Key) io.WriteString(h, stringToSign) signature := hex.EncodeToString(h.Sum(nil)) // Build the form to be returned to the frontend. policyToken := PolicyToken{ Policy: stringToSign, Credential: fmt.Sprintf("%v/%v/%v/%v/aliyun_v4_request", *cred.AccessKeyId, date, region, product), SignatureVersion: "OSS4-HMAC-SHA256", Signature: signature, Date: utcTime.UTC().Format("20060102T150405Z"), SecurityToken: *cred.SecurityToken, } response, err := json.Marshal(policyToken) if err != nil { fmt.Println("json err:", err) } fmt.Println(string(response)) // Return the form. return string(response) }
PHP
Use the following example to calculate the V4 signature on the PHP server side.
Configure environment variables.
NoteReplace
<ALIBABA_CLOUD_ACCESS_KEY_ID>and<ALIBABA_CLOUD_ACCESS_KEY_SECRET>with the AccessKey ID and AccessKey secret of a RAM user, respectively. For more information about how to create an AccessKey ID and an AccessKey secret, see Create an AccessKey.Replace
<ROLE_ARN>with the ARN of the target role. On the Roles page, click the name of the target RAM role and find its ARN in the Basic Information section.
macOS/Linux/Unix
export OSS_ACCESS_KEY_ID=<ALIBABA_CLOUD_ACCESS_KEY_ID> export OSS_ACCESS_KEY_SECRET=<ALIBABA_CLOUD_ACCESS_KEY_SECRET> export OSS_STS_ROLE_ARN=<ROLE_ARN>Windows
set OSS_ACCESS_KEY_ID=<ALIBABA_CLOUD_ACCESS_KEY_ID> set OSS_ACCESS_KEY_SECRET=<ALIBABA_CLOUD_ACCESS_KEY_SECRET> set OSS_STS_ROLE_ARN=<ROLE_ARN>
Configure dependencies.
composer require alibabacloud/credentialsSample code.
<?php require_once 'vendor/autoload.php'; use AlibabaCloud\Credentials\Credential; // Set the response header to JSON format. header('Content-Type: application/json'); try { // Set the region of the bucket. $region = 'cn-hangzhou'; // Set the bucket name. $bucket = 'bucketName'; $product = 'oss'; // Obtain credentials from environment variables. $accessKeyId = getenv('ALIBABA_CLOUD_ACCESS_KEY_ID'); $accessKeySecret = getenv('ALIBABA_CLOUD_ACCESS_KEY_SECRET'); $roleArn = getenv('ROLE_ARN'); if (!$accessKeyId || !$accessKeySecret || !$roleArn) { throw new Exception('Environment variables are not configured correctly.'); } // Create an Alibaba Cloud credential configuration. $config = new Credential\Config([ 'type' => 'ram_role_arn', 'accessKeyId' => $accessKeyId, 'accessKeySecret' => $accessKeySecret, 'roleArn' => $roleArn, 'roleSessionName' => 'role_session_name', 'policy' => '', // Set the expiration time of the temporary access credential to 3600 seconds. 'roleSessionExpiration' => 3600, ]); // Create a credential object based on the configuration. $credential = new Credential($config); // Obtain credential information from the credential object. $cred = $credential->getCredential(); // Get the current UTC time. $utcTime = new DateTime('now', new DateTimeZone('UTC')); // Format the current date to Ymd, for example, 20240101. $date = $utcTime->format('Ymd'); // Clone the current time object to set the expiration time. $expiration = clone $utcTime; // Set the expiration time to 1 hour from the current time. $expiration->add(new DateInterval('PT1H')); // Build the policy. // The example policy form fields list only some of the required fields. For more information about other fields, see Signature Version 4 documentation: https://help.aliyun.com/zh/oss/developer-reference/signature-version-4-recommend. $policyMap = [ "expiration" => $expiration->format('Y-m-d\TH:i:s.000\Z'), "conditions" => [ ["bucket" => $bucket], ["x-oss-signature-version" => "OSS4-HMAC-SHA256"], ["x-oss-credential" => sprintf("%s/%s/%s/%s/aliyun_v4_request", $cred->getAccessKeyId(), $date, $region, $product)], ["x-oss-date" => $utcTime->format('Ymd\THis\Z')], ["x-oss-security-token" => $cred->getSecurityToken()], ], ]; // Convert the policy to a JSON-formatted string. $policy = json_encode($policyMap); // Base64-encode the policy string to get the string-to-sign. $stringToSign = base64_encode($policy); // Build the signing key by concatenating the fixed string "aliyun_v4" and the AccessKey secret. $signingKey = "aliyun_v4" . $cred->getAccessKeySecret(); $h1Key = hash_hmac('sha256', $date, $signingKey, true); $h2Key = hash_hmac('sha256', $region, $h1Key, true); $h3Key = hash_hmac('sha256', $product, $h2Key, true); $h4Key = hash_hmac('sha256', 'aliyun_v4_request', $h3Key, true); // Use h4Key to perform HMAC-SHA256 signing on the string-to-sign to get the final signature. $signature = hash_hmac('sha256', $stringToSign, $h4Key); // Build the form data to be returned to the frontend, including the policy, signature version, credential, date, signature, and security token. $responseData = [ 'policy' => $stringToSign, "x_oss_signature_version" => "OSS4-HMAC-SHA256", "x_oss_credential" => sprintf("%s/%s/%s/%s/aliyun_v4_request", $cred->getAccessKeyId(), $date, $region, $product), "x_oss_date" => $utcTime->format('Ymd\THis\Z'), "signature" => $signature, "security_token" => $cred->getSecurityToken() ]; echo json_encode($responseData); } catch (Exception $e) { http_response_code(500); echo json_encode(['error' => $e->getMessage()]); }
Step 2: Configure the WeChat mini program
To prevent WeChat from blocking requests from the mini program to OSS, you must configure the bucket domain name as a valid domain name on the WeChat mini program platform.
Go to the Bucket List page. Select the destination bucket. In the navigation pane on the left, click Overview. In the Endpoint section, copy the Bucket Domain Name.
Log on to the WeChat Official Accounts Platform and set the valid upload and download domain names to the public endpoint of the bucket, as shown in the figure.
NoteIn a real-world business scenario, we recommend that you bind your custom domain name to the public endpoint provided by OSS. This lets you access files in your OSS bucket using the custom domain name. For more information about the configuration steps, see Bind a custom domain name.
On the WeChat mini program client, use the V4 signature credential information obtained from the ECS server to send a request to upload a file to OSS.
NoteFor more information about upload parameters, see Signature Version 4 and Form fields.
The following is the sample code of the index.js file for uploading a file from the mini program client. For the complete sample project, deploy uploadoss.zip.
Page({ data: { key: 'filename.txt', // The name of the file to be uploaded. You can also specify a directory for it. For example, to upload filename.txt to the youfolder directory, enter /youfolder/filename.txt. policy: '', xOssSecurityToken: '', xOssSignatureVersion: '', xOssCredential: '', xOssDate: '', xOssSignature: '' }, // Method to upload the file. uploadFileToOSS(filePath, callback) { const { key, policy, xOssSecurityToken, xOssSignatureVersion, xOssCredential, xOssDate, xOssSignature } = this.data; const apiUrl='http://<ECS instance public IP address>:<port>/generate_signature' // Replace the IP address and port number with the actual public IP address and port number of your server. // Send a request to obtain signature information. wx.request({ url: apiUrl, success: (res) => { this.data.xOssSignatureVersion = res.data.x_oss_signature_version; this.data.xOssCredential = res.data.x_oss_credential; this.data.xOssDate = res.data.x_oss_date; this.data.xOssSignature = res.data.signature; this.data.xOssSecurityToken = res.data.security_token; this.data.policy = res.data.policy; // This example lists only the required upload parameters. For more information about other parameters, see: // PostObject documentation: https://help.aliyun.com/zh/oss/developer-reference/postobject // Signature Version 4 documentation: https://help.aliyun.com/zh/oss/developer-reference/signature-version-4-recommend const formData = { key, // The name of the uploaded file. policy: this.data.policy, // Form field. 'x-oss-signature-version': this.data.xOssSignatureVersion, // Specifies the signature version and algorithm. 'x-oss-credential': this.data.xOssCredential, // Specifies the parameter set for deriving the key. 'x-oss-date': this.data.xOssDate, // The request time. 'x-oss-signature': this.data.xOssSignature, // The signature authentication description. 'x-oss-security-token': this.data.xOssSecurityToken, // The security token. success_action_status: "200" // The response status code after a successful upload. }; // Send a request to upload the file. wx.uploadFile({ url: 'https://examplebucket.oss-cn-hangzhou.aliyuncs.com', // This domain name is for example purposes only. Replace it with the domain name of your destination bucket. filePath: filePath, name: 'file', // The value is fixed to file. formData: formData, success(res) { console.log('Upload response:', res); if (res.statusCode === 200) { callback(null, res.data); // Upload successful. } else { console.error('Upload failed, status code:', res.statusCode); console.error('Failed response:', res); callback(res); // Upload failed. Return the response. } }, fail(err) { console.error('Upload failed:', err); // Output the error message. wx.showToast({ title: 'Upload failed. Please try again!', icon: 'none' }); callback(err); // Call the callback to handle the error. } }); }, fail: (err) => { console.error('Failed to request the API operation:', err); wx.showToast({ title: 'Failed to obtain upload parameters. Please try again!', icon: 'none' }); } }); }, // The code logic for uploading a file is triggered when the upload file button is clicked. chooseAndUploadFile() { wx.chooseMessageFile({ count: 1, // Select one file. type: 'all', // All file types are supported. success: (res) => { console.log('Selected file:', res.tempFiles); // Output the information of the selected file. if (res.tempFiles.length > 0) { const tempFilePath = res.tempFiles[0].path; // Get the path of the selected file. console.log('Selected file path:', tempFilePath); // Output the file path. this.uploadFileToOSS(tempFilePath, (error, data) => { if (error) { wx.showToast({ title: 'Upload failed!', icon: 'none' }); console.error('Upload failed:', error); // Output the specific error message. } else { wx.showToast({ title: 'Upload successful!', icon: 'success' }); console.log('Upload successful:', data); // Output the data after a successful upload. } }); } else { wx.showToast({ title: 'No file selected!', icon: 'none' }); } }, fail: (err) => { wx.showToast({ title: 'Failed to select file!', icon: 'none' }); console.error('Failed to select file:', err); // Output the error message for file selection. } }); } });
Result verification
After compiling and running the code, click Upload File on the WeChat mini program interface.
On the Bucket List page, select the bucket to which the file was uploaded. In the upper-right corner, click . The file that you uploaded from the mini program is displayed in the upload list.
icon in the Actions column, and then click Grant/Revoke RAM Role.