Obtain signature information from the application server and upload data to OSS

Updated at: 2025-02-06 09:15

You can call the PostObject operation to upload files on web clients to Object Storage Service (OSS). In this case, you can use the signature generated on the application server to ensure the security of file uploads. You can also configure an upload policy to meet your business requirements.

Solution overview

The following figure shows how to generate a signature on an application server and upload files on a web client to OSS.

image

To obtain signature information from the application server and upload data to OSS, perform the following steps:

Note

In this process, temporary access credentials are used. This prevents the AccessKey pair of the application server from being leaked and ensures the security of file uploads.

  1. Configure OSS: In the OSS console, create a bucket to store uploaded files. Then, configure a cross-origin resource sharing (CORS) rule for the bucket to allow cross-origin requests from the server.

  2. Configure the server: Call the corresponding operation of Security Token Service (STS) to obtain a pair of temporary access credentials. Then, use the temporary access credentials and the upload policy preset on the server to generate a signature and authorize the user to upload files within a specific period of time. The preset upload policy includes information such as bucket name, directory path, and expiration time.

  3. Configure the web client: Create an HTML form and use the form to submit the signature and upload files to OSS.

Demo projects

Procedure

Step 1: Configure OSS

1. Create a bucket

Create an OSS bucket to store data that is uploaded by the web application from a browser.

  1. Log on to the OSS console.

  2. In the left-side navigation pane, click Buckets. On the Buckets page, click Create Bucket.

  3. In the Create Bucket panel, configure the parameters. The following table describes the parameters.

    Parameter

    Example

    Bucket Name

    web-direct-upload

    Region

    China (Hangzhou)

  4. Click Create.

2. Configure a CORS rule

Configure a CORS rule for the bucket that you created.

  1. On the Buckets page, click the name of the bucket that you want to manage.

  2. In the left-side navigation tree of the page that appears, choose Content Security > CORS. On the CORS page, click Create Rule.

  3. In the Create Rule panel, configure the parameters. The following table describes the parameters.

    Parameter

    Example

    Source

    *

    Allowed Methods

    POST, PUT, and GET

    Allowed Headers

    *

  1. Click OK.

Step 2: Configure the server

Note

In actual deployment, if you have your own application server, you do not need to perform operations in the "Preparations: Create an Elastic Compute Service (ECS) instance as the application server" section and are redirected to 1. Configure user permissions.

Preparations: Create an Elastic Compute Service (ECS) instance as the application server

Operation 1: Create an ECS instance

Go to the Custom Launch tab of the instance buy page in the ECS console. Then, create or select the basic resources required by the ECS instance based on the following descriptions.

  1. Select a region and a billing method

    1. Select a billing method based on your business requirements. In this example, the pay-as-you-go billing method is selected. This billing method allows more flexible operations than other billing methods.

    2. Select a region based on the network latency requirements of your business. For low network latency and high access speed, we recommend that you select a region in close proximity to your users. In this example, the China (Hangzhou) region is selected.

      image

  1. Create a virtual private cloud (VPC) and a vSwitch

    When you create a VPC, select the region in which you created the ECS instance and plan CIDR blocks based on your business requirements. In this example, a VPC and a vSwitch are created in the China (Hangzhou) region. After you create a VPC and a vSwitch, go back to the Custom Launch tab of the instance buy page in the ECS console, refresh the VPC and vSwitch drop-down lists, and then select the VPC and vSwitch that you created.

    Note

    You can also create a vSwitch during VPC creation.

    image

    image

    image

  1. Select an instance type and an image

    Select an instance type and an image. The operating system version included in the image is installed on the instance during instance creation. In this example, the cost-effective ecs.e-c1m1.large instance type and the Alibaba Cloud Linux 3.2104 LTS 64-bit public image are selected.

    image

  1. Select storage

    Configure a system disk and data disks for the ECS instance based on your business requirements. This topic describes how to set up a simple web service on the ECS instance, which requires only a system disk to store the operating system of the instance.

    image

  1. Assign a public IP address

    To provide Internet connectivity to the ECS instance, select Assign Public IPv4 Address to assign a public IP address to the instance. Alternatively, you can also associate an elastic IP address (EIP) with the ECS instance after you create the instance. For more information, see Associate an EIP with an ECS instance.

    Note
    • If you do not assign a public IP address to or associate an EIP with the ECS instance, you cannot access the instance over SSH or Remote Desktop Protocol (RDP) or test the web service that is deployed on the instance over the Internet.

    • After you select Assign Public IPv4 Address, configure the Bandwidth Billing Method parameter to specify a billing method for network usage. In this example, the Bandwidth Billing Method parameter is set to Pay-by-traffic. In the pay-by-traffic billing method, you are charged based on the amount of data transferred over the Internet. For more information, see Public bandwidth.

    image

  1. Create a security group

    Create a security group for the ECS instance. A security group serves as a virtual firewall that can control inbound and outbound traffic for ECS instances. When you create a security group, open the following ports to allow access to the ECS instance:

    Open IPv4 Ports/Protocols: select SSH (TCP:22), RDP (TCP:3389), HTTP (TCP:80), and HTTPS (TCP:443).

    Note
    • In the Open IPv4 Ports/Protocols section, select the ports that must be open for the applications that run on the ECS instance.

    • By default, a rule that references 0.0.0.0/0 as a source address is created in the new security group. 0.0.0.0/0 represents all IP addresses. The rule allows access to the ECS instance from all IP addresses on the specified ports. After you create the instance, we recommend that you modify the rule to allow access to the instance from only specific IP addresses. For more information, see Modify a security group rule.

    image

  1. Create a key pair

    1. You can bind key pairs to ECS instances and use the key pairs as security credentials to authenticate your identity when you log on to the instances. After you create a key pair, download the private key of the key pair for subsequent use during instance connection. For more information, see Connect to the ECS instance. After you create a key pair, go back to the Custom Launch tab of the instance buy page, refresh the Key Pair drop-down list, and then select the key pair that you created.

    2. root is the highest-privileged account in the operating system. If you select root as the logon username, security risks may arise. We recommend that you select ecs-user as the logon username.

      Note

      After you create the key pair, the private key of the key pair is automatically downloaded. View the download history of your browser and save the private key file, which is in the .pem format.

      image

  1. Create and view an ECS instance

    After you create or select the required basic resources, read and select ECS Terms of Service and Product Terms of Service. Then, click Create Order. In the Success message, click Console to view the created ECS instance on the Instance page. Save the following data for later use:

    • Instance ID: You can search for the ECS instance by instance ID.

    • Region: You can search for the ECS instance by region.

    • Public IP address: You can use the public IP address of the ECS instance to check whether a web service is deployed on the instance.

    imageimage

Operation 2: Connect to the ECS instance

  1. On the Instance page in the ECS console, find the ECS instance that you created based on its region and ID. Then, click Connect in the Actions column.image

  2. In the Remote connection dialog box, click Sign in now in the Workbench section.image

  3. In the Instance Login dialog box, set the Authentication parameter to SSH Key Authentication, set the Username parameter to ecs-user, enter or upload the private key file that you downloaded when you created the key pair, and then click OK.

    Note

    The private key file was automatically downloaded to your on-premises computer when you created the key pair. Check the download history of your browser to find the private key file in the .pem format.

    image

  4. If a page similar to that shown in the following figure appears, you are logged on to the ECS instance.image

1. Configure user permissions

Note

To prevent local files from failing to be uploaded due to unauthorized operations after the deployment is complete, we recommend that you perform the following steps to create a Resource Access Management (RAM) user and grant the required permissions to the RAM user.

Operation 1: Create a RAM user in the RAM console

Create a RAM user and obtain the AccessKey pair of the RAM user. The AccessKey pair is a long-term access credential that is required to access and manage the application server.

  1. Log on to the RAM console by using an Alibaba Cloud account or as an account administrator.

  2. In the left-side navigation pane, choose Identities > Users.

  3. On the Users page, click Create User.

  4. Configure the Logon Name and Display Name parameters.

  5. In the Access Mode section, select Using permanent AccessKey to access and click OK.

Important

You can obtain the AccessKey secret of a RAM user only when you create the RAM user. You must keep the AccessKey secret secure to prevent credential leaks.

  1. Click Copy in the Actions column of the page that appears to copy the AccessKey pair and paste it to a securely stored file.

Operation 2: Grant the RAM user the permissions to call the AssumeRole operation in the RAM console

Grant the RAM user the permissions to call the AssumeRole operation. This way, the RAM user can obtain temporary access credentials by assuming a RAM role.

  1. In the left-side navigation pane, choose Identities > Users.

  2. On the Users page, find the RAM user and click Add Permissions in the Actions column.

  3. In the Policy section of the Grant Permission panel, select the AliyunSTSAssumeRoleAccess system policy.

    Note

    The AliyunSTSAssumeRoleAccess policy allows a RAM user to call the AssumeRole operation. The permissions of the policy are independent of the permissions required to allow the RAM user to obtain temporary access credentials from STS and initiate requests to OSS.

    image.png

  4. Click Grant permissions.

Operation 3: Create a RAM role in the RAM console

Create a RAM role for the Alibaba Cloud account and obtain the Alibaba Cloud Resource Name (ARN) of the RAM role. The RAM role is assumed by the RAM user later.

  1. In the left-side navigation pane, choose Identities > Roles.

  2. Click Create Role. In the Create Role wizard, set the Select Trusted Entity parameter to Alibaba Cloud Account and click Next.

  3. In the Configure Role step, enter a name in the RAM Role Name field and set the Select Trusted Alibaba Cloud Account parameter to Current Alibaba Cloud Account.

  4. Click OK. After you create the role, click Close.

  5. On the Roles page, enter the role name in the search box. Example: oss-web-upload. Then, click the name of the role.

  6. In the Basic Information section, click Copy next to the ARN field to record the ARN of the RAM role.

Operation 4: Create a custom policy in the RAM console

Create a custom policy based on the principle of least privilege to grant the RAM role the permissions to upload data only to a specific bucket.

  1. In the left-side navigation pane, choose Permissions > Policies.

  2. On the Policies page, click Create Policy.

  3. On the Create Policy page, click JSON. Copy the following sample script and paste it into the code editor. Replace <BucketName> with the name of the bucket that you created, which is web-direct-upload.

    {
      "Version": "1",
      "Statement": [
        {
          "Effect": "Allow",
          "Action": "oss:PutObject",
          "Resource": "acs:oss:*:*:<BucketName>/*"
        }
      ]
    }
  4. After you configure the policy, click Next to edit policy information.

  5. In the Basic Information section, specify the policy name and click OK.

Operation 5: Attach the custom policy to the RAM role in the RAM console

Attach the custom policy to the RAM role. This way, the RAM role can provide the required permissions when the RAM role is assumed.

  1. In the left-side navigation pane, choose Identities > Roles.

  2. On the Roles page, find the RAM role and click Grant Permission in the Actions column.

  3. In the Policy section of the Grant Permission panel, select Custom Policy from the drop-down list, and then select the custom policy.

  4. Click Grant permissions.

2. Obtain the temporary access credentials and calculate the signature on the server

You can refer to the following sample code to calculate V4 signatures in PostObject requests on the server. For more information, see (Recommended) Include a V4 signature in a PostObject request. For information about the configurations of the policy form field in a PostObject request, see policy.

Java
Python

Add the following dependencies to the Maven project:

<!-- 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>
<dependency>
  <groupId>com.aliyun</groupId>
  <artifactId>sts20150401</artifactId>
  <version>1.1.6</version>
</dependency>

You can refer to the following sample code to obtain temporary access credentials and calculate signatures in PostObject requests:

import com.aliyun.sts20150401.models.AssumeRoleResponse;
import com.aliyun.sts20150401.models.AssumeRoleResponseBody;
import com.aliyun.tea.TeaException;
import com.aliyun.oss.common.utils.BinaryUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.codec.binary.Base64;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.*;

@Controller
public class WebController {

    // The basic information. Replace the values with the actual bucket name, region ID, and host endpoint. 
    String bucket = "examplebucket";
    String region = "cn-hangzhou";
    String host = "http://examplebucket.oss-cn-hangzhou.aliyuncs.com";
    
    // The prefix of the names of objects uploaded to OSS. 
    String upload_dir = "dir";
    
    // The validity period of the temporary credentials. Unit: seconds. 
    Long expire_time = 3600L;

    /**
     * The expiration time is calculated based on the specified validity period. Unit: seconds. 
     * @param seconds: the validity period, in seconds. 
     * @return: the time string in the ISO 8601 standard. Example: 2014-12-01T12:00:00.000Z. 
     */
    public static String generateExpiration(long seconds) {
        // The current timestamp. Unit: seconds.
        long now = Instant.now().getEpochSecond();
        // The timestamp used to calculate the expiration time.
        long expirationTime = now + seconds;
        // Convert the timestamp into an instant object and format it in the ISO 8601 format.
        Instant instant = Instant.ofEpochSecond(expirationTime);
        // Define the time zone.
        ZoneId zone = ZoneId.systemDefault();  // The default time zone of the system is used.
        // Convert the instance object into a ZonedDateTime object.
        ZonedDateTime zonedDateTime = instant.atZone(zone);
        // Define the datetime format. Example: 2023-12-03T13:00:00.000Z.
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
        // Format the datetime.
        String formattedDate = zonedDateTime.format(formatter);
        // Output the result.
        return formattedDate;
    }
    // Initialize an STSClient instance.
    public static com.aliyun.sts20150401.Client createStsClient() throws Exception {
        // If the project code is leaked, the AccessKey pair may be leaked and the security of all resources within your account may be compromised. The following sample code is provided only for reference. 
        // We recommend that you use STS tokens, which provide higher security. 
        com.aliyun.teaopenapi.models.Config config = new com.aliyun.teaopenapi.models.Config()
                    // Required. Make sure that the OSS_ACCESS_KEY_ID environment variable is configured. 
                .setAccessKeyId(System.getenv("OSS_ACCESS_KEY_ID"))
                // Required. Make sure that the OSS_ACCESS_KEY_SECRET environment variable is configured. 
                .setAccessKeySecret(System.getenv("OSS_ACCESS_KEY_SECRET"));
        // Specify the endpoint of the region in which the bucket is located. For more information, visit https://api.aliyun.com/product/Sts.
        config.endpoint = "sts.cn-hangzhou.aliyuncs.com";
        return new com.aliyun.sts20150401.Client(config);
    }

    // Obtain an STS token.
    public static AssumeRoleResponseBody.AssumeRoleResponseBodyCredentials getCredential() throws Exception {
        com.aliyun.sts20150401.Client client = WebController.createStsClient();
        com.aliyun.sts20150401.models.AssumeRoleRequest assumeRoleRequest = new com.aliyun.sts20150401.models.AssumeRoleRequest()
        // Required. Make sure that the OSS_STS_ROLE_ARN environment variable is configured.
                .setRoleArn(System.getenv("OSS_STS_ROLE_ARN"))
                .setRoleSessionName("yourRoleSessionName");// Specify a custom session name.
        com.aliyun.teautil.models.RuntimeOptions runtime = new com.aliyun.teautil.models.RuntimeOptions();
        try {
            // Write your own code to display the response of the API operation if necessary.
            AssumeRoleResponse response = client.assumeRoleWithOptions(assumeRoleRequest, runtime);
            // The AccessKey ID, AccessKey Secret, and security token that are required for subsequent operations are included in credentials. 
            return response.body.credentials;
        } catch (TeaException error) {
            // Handle exceptions with caution in actual business scenarios and do not ignore the exceptions in your project. In this example, exceptions are provided for reference only. 
            // The error message.
            System.out.println(error.getMessage());
            // The URL for troubleshooting.
            System.out.println(error.getData().get("Recommend"));
            com.aliyun.teautil.Common.assertAsString(error.message);
        } catch (Exception _error) {
            TeaException error = new TeaException(_error.getMessage(), _error);
            // Handle exceptions with caution in actual business scenarios and do not ignore the exceptions in your project. In this example, exceptions are provided for reference only. 
            // The error message.
            System.out.println(error.getMessage());
            // The URL for troubleshooting.
            System.out.println(error.getData().get("Recommend"));
            com.aliyun.teautil.Common.assertAsString(error.message);
        }
        return null;
    }
    @GetMapping("/get_post_signature_for_oss_upload")
    public ResponseEntity<Map<String, String>> getPostSignatureForOssUpload() throws Exception {
        AssumeRoleResponseBody.AssumeRoleResponseBodyCredentials sts_data = getCredential();

        String accesskeyid =  sts_data.accessKeyId;
        String accesskeysecret =  sts_data.accessKeySecret;
        String securitytoken =  sts_data.securityToken;
        System.out.println("sts accesskeyid:"+accesskeyid);
        System.out.println("sts accesskeysecret:"+accesskeysecret);
        System.out.println("sts securitytoken:"+securitytoken);

        // Obtain the current date in the x-oss-credential header. The value is in the yyyyMMdd format.
        LocalDate today = LocalDate.now();
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd");
        String date = today.format(formatter);

        // Obtain the x-oss-date header.
        ZonedDateTime now = ZonedDateTime.now().withZoneSameInstant(java.time.ZoneOffset.UTC);
        DateTimeFormatter formatter2 = DateTimeFormatter.ofPattern("yyyyMMdd'T'HHmmss'Z'");
        String x_oss_date = now.format(formatter2);

        // Step 1: Create a policy. 
        String x_oss_credential = accesskeyid + "/" + date + "/" + region + "/oss/aliyun_v4_request";
        
        ObjectMapper mapper = new ObjectMapper();

        Map<String, Object> policy = new HashMap<>();
        policy.put("expiration", generateExpiration(expire_time));

        List<Object> conditions = new ArrayList<>();

        Map<String, String> bucketCondition = new HashMap<>();
        bucketCondition.put("bucket", bucket);
        conditions.add(bucketCondition);

        Map<String, String> securityTokenCondition = new HashMap<>();
        securityTokenCondition.put("x-oss-security-token", securitytoken);
        conditions.add(securityTokenCondition);

        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", x_oss_credential); // Replace the value with your AccessKey ID.
        conditions.add(credentialCondition);

        Map<String, String> dateCondition = new HashMap<>();
        dateCondition.put("x-oss-date", x_oss_date);
        conditions.add(dateCondition);

        conditions.add(Arrays.asList("content-length-range", 1, 10240000));
        conditions.add(Arrays.asList("eq", "$success_action_status", "200"));
        conditions.add(Arrays.asList("starts-with", "$key", upload_dir));

        policy.put("conditions", conditions);

        String jsonPolicy = mapper.writeValueAsString(policy);

        // Step 2: Create a string to sign. 
        String stringToSign = new String(Base64.encodeBase64(jsonPolicy.getBytes()));
        // System.out.println("stringToSign: " + stringToSign);

        // Step 3: Calculate the signing key. 
        byte[] dateKey = hmacsha256(("aliyun_v4" + accesskeysecret).getBytes(), date);
        byte[] dateRegionKey = hmacsha256(dateKey, region);
        byte[] dateRegionServiceKey = hmacsha256(dateRegionKey, "oss");
        byte[] signingKey = hmacsha256(dateRegionServiceKey, "aliyun_v4_request");
        // System.out.println("signingKey: " + BinaryUtil.toBase64String(signingKey));

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

        Map<String, String> response = new HashMap<>();
        // Add data to the response mapping.
        response.put("version", "OSS4-HMAC-SHA256");
        // This configuration is prone to errors. You must perform Base64 encoding before you upload the policy.
        response.put("policy", stringToSign);
        response.put("credential", x_oss_credential);
        response.put("ossdate", x_oss_date);
        response.put("signature", signature);
        response.put("token", securitytoken);
        response.put("dir", upload_dir);
        response.put("host", host);
        // Return the ResponseEntity class that includes the 200 OK status code to the web client. Then, the client can perform the PostObject operation.
        return ResponseEntity.ok(response);
    }
    public static byte[] hmacsha256(byte[] key, String data) {
        try {
            // Initialize the HMAC key specification, set the algorithm to HMAC-SHA256, and use the provided key. 
            SecretKeySpec secretKeySpec = new SecretKeySpec(key, "HmacSHA256");

            // Obtain a Mac instance and use the getInstance method to set the algorithm to HMAC-SHA256. 
            Mac mac = Mac.getInstance("HmacSHA256");
            // Use the key to initialize the Mac instance. 
            mac.init(secretKeySpec);

            // Calculate the HMAC value. Use the doFinal method to receive the data to be calculated and return the calculation results in arrays. 
            byte[] hmacBytes = mac.doFinal(data.getBytes());

            return hmacBytes;
        } catch (Exception e) {
            throw new RuntimeException("Failed to calculate HMAC-SHA256", e);
        }
    }
}
  • Run the following command to install the dependency:

    pip install flask
    pip install alibabacloud_tea_openapi alibabacloud_sts20150401 alibabacloud_credentials
  • Refer to the following sample code to obtain the temporary access credentials and create an upload policy to calculate signatures in PostObject requests:

    from flask import Flask, render_template, jsonify
    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
    import json
    import base64
    import hmac
    import datetime
    import time
    import hashlib
    
    app = Flask(__name__)
    
    # Configure the OSS_ACCESS_KEY_ID, OSS_ACCESS_KEY_ID, and OSS_STS_ROLE_ARN environment variables. 
    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')
    
    # Specify a session name.
    role_session_name = 'role_session_name'  
    
    # Replace the values with the actual bucket name, region ID, and host endpoint.
    bucket = 'examplebucket'
    region_id = 'cn-hangzhou'
    host = 'http://examplebucket.oss-cn-hangzhou.aliyuncs.com'
    
    // The validity period of the temporary credentials. Unit: seconds.
    expire_time = 3600  
    # Specify the prefix in the name of the object that you want to upload to OSS. 
    upload_dir = 'dir'
    
    def hmacsha256(key, data):
        """
        The function used to calculate the HMAC-SHA256 value.
    
        :param key: The key used to calculate the hash value. The key is of the byte type.
        :param data: The data whose hash value you want to calculate. The data is of the string type.
        :return: The calculated HMAC-SHA256 value. The value is of the byte type.
        """
        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("/")
    def hello_world():
        return render_template('index.html')
    
    @app.route('/get_post_signature_for_oss_upload', methods=['GET'])
    def generate_upload_params():
        # Initialize configurations and pass the credentials.
        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 access 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 access credentials returned by STS.
        temp_access_key_id = token_data['AccessKeyId']
        temp_access_key_secret = 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)
        # Set the expiration time of the request to 3 hours later than the current time.
        dt_obj_plus_3h = dt_obj + datetime.timedelta(hours=3)
    
        # The request time.
        dt_obj_1 = dt_obj.strftime('%Y%m%dT%H%M%S') + 'Z'
        # The request date.
        dt_obj_2 = dt_obj.strftime('%Y%m%d')
        # The expiration time of the request.
        expiration_time = dt_obj_plus_3h.strftime('%Y-%m-%dT%H:%M:%S.000Z')
        
        # Create a policy and generate a signature.
        policy = {
            "expiration": expiration_time,
            "conditions": [
                ["eq", "$success_action_status", "200"],
                {"x-oss-signature-version": "OSS4-HMAC-SHA256"},
                {"x-oss-credential": f"{temp_access_key_id}/{dt_obj_2}/cn-hangzhou/oss/aliyun_v4_request"},
                {"x-oss-security-token": security_token},
                {"x-oss-date": dt_obj_1},  
            ]
        }
        print(dt_obj_1)
        policy_str = json.dumps(policy).strip()
    
        # Step 2: Create a string to sign.
        stringToSign = base64.b64encode(policy_str.encode()).decode()
    
        # Step 3: Calculate the signing key.
        dateKey = hmacsha256(("aliyun_v4" + temp_access_key_secret).encode(), dt_obj_2)
        dateRegionKey = hmacsha256(dateKey, "cn-hangzhou")
        dateRegionServiceKey = hmacsha256(dateRegionKey, "oss")
        signingKey = hmacsha256(dateRegionServiceKey, "aliyun_v4_request")
    
        # Step 4: Calculate the signature.
        result = hmacsha256(signingKey, stringToSign)
        signature = result.hex()
    
        # The response data.
        response_data = {
            'policy': stringToSign,  # The form field.
            'x_oss_signature_version': "OSS4-HMAC-SHA256",  # The signature version and the algorithm used to calculate the signature. Set the value to OSS4-HMAC-SHA256.
            'x_oss_credential': f"{temp_access_key_id}/{dt_obj_2}/cn-hangzhou/oss/aliyun_v4_request",  # The parameter set used to specify information about the derived key.
            'x_oss_date': dt_obj_1,  # The request time.
            'signature': signature,  # The signature description.
            'host': host,
            'dir': upload_dir,
            'security_token': security_token # The security token.
    
        }
        return jsonify(response_data)
    
    if __name__ == "__main__":
        app.run(host="0.0.0.0", port=8000)

Step 3: Configure the web client

Create and submit a form upload request on the web client

After the web client receives the required information from the server, you can use the information to construct a request to create an HTML form. The request is directly sent to OSS for file upload.

Sample response received by the web client

Receive the temporary access credentials and the upload policy returned to the client from the application server.

{
    "dir": "user-dirs",
    "host": "http://examplebucket.oss-cn-hangzhou.aliyuncs.com",
    "policy": "eyJl****",
    "security_token": "CAIS****",
    "signature": "9103****",
    "x_oss_credential": "STS.NSpW****/20241127/cn-hangzhou/oss/aliyun_v4_request",
    "x_oss_date": "20241127T060941Z",
    "x_oss_signature_version": "OSS4-HMAC-SHA256"
}

The following table describes the fields that are included in the message body.

Field

Description

dir

The prefix contained in the names of the objects that you want to upload.

host

The domain name of the bucket.

policy

The policy for form upload. For more information, see POST policy.

security_token

The security token obtained from STS.

signature

The signature string of the policy. For more information, see POST signature.

x_oss_credential

The parameter set used to specify the derived key.

x_oss_date

The time when the request was initiated. The time must follow the ISO 8601 standard. Example: 20231203T121212Z.

x_oss_signature_version

The signature version and the algorithm used to calculate the signature. Set the value to OSS4-HMAC-SHA256.

  • The form request contains file content and parameters returned by the server.

  • The web client can directly communicate with OSS and upload files by using the form request.

Note
  • Except for the file form field, the size of each form field (including key) cannot exceed 8 KB.

  • By default, an existing object that has the same name as the object that you want to upload is overwritten. If you do not want to overwrite the existing object, include the x-oss-forbid-overwrite header in the upload request and set the x-oss-forbid-overwrite header to true. This way, if you upload a file whose name is the same as an existing object in OSS, the upload fails and OSS returns the FileAlreadyExists error code.

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Generate a signature on the server to upload files to OSS</title>
</head>
<body>
<div class="container">
    <form>
        <div class="mb-3">
            <label for="file" class="form-label">Select the file:</label>
            <input type="file" class="form-control" id="file" name="file" required />
        </div>
        <button type="submit" class="btn btn-primary">Upload</button>
    </form>
</div>

<script type="text/javascript">
document.addEventListener('DOMContentLoaded', function () {
    const form = document.querySelector("form");
    const fileInput = document.querySelector("#file");

    form.addEventListener("submit", (event) => {
        event.preventDefault();

        const file = fileInput.files[0];

        if (!file) {
            alert('Select a file to upload. ');
            return;
        }

        const filename = file.name;

        fetch("/get_post_signature_for_oss_upload", { method: "GET" })
            .then((response) => {
                if (!response.ok) {
                    throw new Error("Failed to obtain the signature");
                }
                return response.json();
            })
            .then((data) => {
                let formData = new FormData();
                formData.append("success_action_status", "200");
                formData.append("policy", data.policy);
                formData.append("x-oss-signature", data.signature);
                formData.append("x-oss-signature-version", "OSS4-HMAC-SHA256");
                formData.append("x-oss-credential", data.x_oss_credential);
                formData.append("x-oss-date", data.x_oss_date);
                formData.append("key", data.dir + file.name); // The file name.
                formData.append("x-oss-security-token", data.security_token);
                formData.append("file", file); // The last form field must be "file".

                return fetch(data.host, { 
                    method: "POST",
                    body: formData
                });
            })
            .then((response) => {
                if (response.ok) {
                    console.log("Uploaded");
                    alert("File uploaded");
                } else {
                    console.log("Upload failed", response);
                    alert("Failed to upload the file. Try again later");
                }
            })
            .catch((error) => {
                console.error("An error occurred:", error);
            });
    });
});
</script>
</body>
</html>
  • The HTML form contains a file input box and a Submit button. You can select a file to upload and submit the form.

  • When you submit the form, the default form submission is blocked by using JavaScript code, and the signature information required for the upload is obtained from the server by using an Asynchronous JavaScript and XML (AJAX) request.

  • After you obtain the signature information, construct a FormData object that contains all the required form fields.

  • You can use the fetch method to send a POST request to the URL of the OSS service and upload files.

If the files are uploaded, the "File uploaded" message is displayed. If the files fail to be uploaded, an error message is displayed.

Verify the result

After you complete the preceding steps, you can access the application server to generate a signature and upload files on a web client to OSS.

  1. Enter the URL of the application server in a browser, select the file that you want to upload, and then click the Upload button to upload the file.

  2. On the Buckets page, click the name of the bucket that you created to store uploaded files. On the page that appears, view the file that you uploaded from the web client.

    image

Suggested configurations

Configure sensitive information in environment variables

To prevent risks caused by explicitly specifying sensitive information, such as accessKeyId, accessKeySecret, and roleArn, in the code, we recommend that you configure the sensitive information in environment variables.

You can add the environment variables as temporary environment variables and use them only in the current session.

Linux
macOS
Windows
  1. Run the following command:

    export OSS_ACCESS_KEY_ID="your-access-key-id"
    export OSS_ACCESS_KEY_SECRET="your-access-key-secret"
    export OSS_STS_ROLE_ARN="your-role-arn"
  2. Run the following command to check whether the environment variables take effect:

    echo $OSS_ACCESS_KEY_ID
    echo $OSS_ACCESS_KEY_SECRET
    echo $OSS_STS_ROLE_ARN
  1. Run the following command:

    export OSS_ACCESS_KEY_ID="your-access-key-id"
    export OSS_ACCESS_KEY_SECRET="your-access-key-secret"
    export OSS_STS_ROLE_ARN="your-role-arn"
  2. Run the following command to check whether the environment variables take effect:

    echo $OSS_ACCESS_KEY_ID
    echo $OSS_ACCESS_KEY_SECRET
    echo $OSS_STS_ROLE_ARN
  1. Run the following command in CMD:

    set OSS_ACCESS_KEY_ID "your-access-key-id"
    set OSS_ACCESS_KEY_SECRET "your-access-key-secret"
    set OSS_STS_ROLE_ARN "your-role-arn"
  2. Open a new window.

  3. Run the following command to check whether the environment variable takes effect in the new window:

    echo $OSS_ACCESS_KEY_ID
    echo $OSS_ACCESS_KEY_SECRET
    echo $OSS_STS_ROLE_ARN

Add signatures on the server, configure an upload callback, and directly transfer data

If you want to obtain more information about the uploaded file, such as the file name and image size, you can configure an upload callback. This way, information about files is automatically received after the files are uploaded. For more information, see Add signatures on the server, configure upload callbacks, and directly transfer data.

Specify the server URL as the source when configuring a CORS rule

In the preceding steps, * is specified as the source when configuring a CORS rule. For security reasons, we recommend that you narrow down the scope of the source. For example, when you configure a CORS rule, you can set the Source parameter to the URL of your application server. This way, cross-origin operations can be performed only for requests from the specified server.

Parameter

Example

Source

http:// The URL of the application server

Allowed Methods

POST, PUT, and GET

Allowed Headers

*

Resource cleaning

To build the web application, you created an ECS instance, an OSS bucket, a RAM user, and a RAM role. After you complete verification, you can release these resources to avoid unnecessary charges and eliminate relevant security risks.

Release the ECS instance

If you no longer require the ECS instance that you created, you can release the instance. After the ECS instance is released, billing for the instance stops, and data of the instance is lost and cannot be restored. Procedure:

  1. Go to the Instance page in the ECS console, find the ECS instance based on its region and instance ID, and then click the image icon in the Actions column.

  2. Choose Instance Status > Release.image

  3. In the Release dialog box, set Release Mode to Release Now and click Next.

  4. Confirm the associated resources that you want to release, read the notes about the data risks, select "I am aware of the instances and their associated resources to be released and understand the data risks", and then click OK.

Note
  • When the ECS instance is released, the system disk of the instance is released. If a public IP address was assigned to the instance, the IP address is also released.

  • When the ECS instance is released, the associated security group, vSwitch, and VPC are not released. You are not charged for the security group, vSwitch, or VPC. You can retain or release them based on your business requirements.

  • If an EIP is associated with the ECS instance, the EIP is retained when the instance is released. You are charged for the EIP. You can retain or release the EIP based on your business requirements.

Delete the bucket

  1. Log on to the OSS console.

  2. In the left-side navigation pane, click Buckets. On the Buckets page, click the name of the bucket that you want to delete.

  3. Delete all objects in the bucket and then delete the bucket.

  4. In the left-side navigation tree, click Delete Bucket. On the Delete Bucket page, follow the on-screen instructions to delete the bucket.

Delete the RAM user

  1. Log on to the RAM console as a RAM user who has admin privileges.

  2. In the left-side navigation pane, choose Identities > Users.

  3. On the Users page, find the RAM user that you want to delete and click Delete in the Actions column.

    You can also select multiple RAM users and click Delete User below the RAM user list to move all the selected RAM users to the recycle bin at the same time.

  4. In the Delete User dialog box, read the statement on the impact of deletion, enter the name of the RAM user, and then click Move to Recycle Bin.

Delete the RAM role

  1. Log on to the RAM console as a RAM user who has admin privileges.

  2. In the left-side navigation pane, choose Identities > Roles.

  3. On the Roles page, find the RAM role that you want to delete and click Delete Role in the Actions column.

  4. In the Delete Role dialog box, enter the name of the RAM role and click Delete Role.

    Note

    If a policy is attached to the RAM role, the policy is detached when you delete the RAM role.

FAQ

Does this solution support multipart upload and resumable upload?

No, this solution does not support multipart upload or resumable upload because files in the solution are uploaded by using an HTML form. For information about how to implement multipart upload or resumable upload, see Obtain temporary access credentials from STS on the application server.

How can I prevent uploaded files from being overwritten?

If you want to prevent uploaded files from being overwritten, you can add the x-oss-forbid-overwrite header in the request and set the header to true. Example:

formData.append('x-oss-forbid-overwrite', 'true');
  • On this page (1, T)
  • Solution overview
  • Demo projects
  • Procedure
  • Step 1: Configure OSS
  • Step 2: Configure the server
  • Step 3: Configure the web client
  • Verify the result
  • Suggested configurations
  • Configure sensitive information in environment variables
  • Add signatures on the server, configure an upload callback, and directly transfer data
  • Specify the server URL as the source when configuring a CORS rule
  • Resource cleaning
  • Release the ECS instance
  • Delete the bucket
  • Delete the RAM user
  • Delete the RAM role
  • FAQ
  • Does this solution support multipart upload and resumable upload?
  • How can I prevent uploaded files from being overwritten?
Feedback