您可以使用PostObject介面,將檔案直接從 Web 端上傳到 OSS,伺服器產生的簽名為直傳操作提供安全保障,同時支援配置上傳策略(Policy)以限制上傳操作並滿足業務需求。
方案概覽
服務端產生簽名實現Web端直傳的過程如下:
要實現服務端簽名直傳,只需3步:
由於使用了臨時訪問憑證,整個過程中不會泄露商務服務器的長期密鑰,保證了檔案上傳的安全性。
配置OSS:配置OSS,在控制台建立一個Bucket,用於儲存使用者上傳的檔案。同時,為 Bucket 配置跨域資源共用(CORS) 規則,以允許來自服務端的跨域請求。
佈建服務端:佈建服務端,調用STS服務擷取一個臨時訪問憑證,然後使用臨時訪問憑證和服務端預設的上傳策略(如Bucket名稱、目錄路徑、到期時間等)產生簽名授權使用者在一定時間內進行檔案上傳。
配置Web端:配置Web端,構造HTML表單請求,通過表單提交使用簽名將檔案上傳到OSS。
樣本工程
Java完整樣本工程請參見:server-signed-direct-upload-java.zip
Python完整樣本工程請參見:server-signed-direct-upload-python.zip
Node.js完整樣本工程請參見:server-signed-direct-upload-nodejs.zip
Go完整樣本工程請參見:server-signed-direct-upload-go.zip
PHP完整樣本工程請參見:server-signed-direct-upload-php.zip
操作步驟
步驟一:配置OSS
一、建立Bucket
建立一個OSS Bucket,用於儲存Web應用在瀏覽器環境中直接上傳的檔案。
登入OSS管理主控台。
在左側導覽列,單擊Bucket 列表,然後單擊建立 Bucket。
在建立 Bucket面板,選擇快捷建立,按如下說明配置各項參數。
參數
樣本值
Bucket名稱
web-direct-upload
地域
華東1(杭州)
點擊完成建立。
二、配置CORS規則
為建立的OSS Bucket配置CORS規則。
訪問Bucket列表,然後單擊目標Bucket名稱。
在跨域設置頁面,單擊創建規則。
在跨域規則面板,按以下說明設定跨域規則。
參數
樣本值
來源
*
允許Methods
POST、PUT、GET
允許Headers
*
單擊確定。
步驟二:佈建服務端
在實際部署時,如果您已經有自己的商務服務器,則無需進行準備工作,直接跳轉到一、配置使用者權限。
準備工作:建立一台ECS執行個體作為商務服務器
操作一:建立ECS執行個體
請您進入自訂購買頁面,並根據如下各模組的內容,建立或選擇購買ECS執行個體所需的基礎資源。
選擇地區 & 付費類型
根據業務需求,選擇合適的付費類型。本文選擇隨用隨付模式,此模式操作相對靈活。
基於業務情境對時延的要求,選擇地區。通常來說離ECS執行個體的物理距離越近,網路時延越低,訪問速度越快。本文以選擇華東1(杭州)為例。
建立Virtual Private Cloud & 交換器
建立VPC時,請您選擇和ECS相同的地區,並根據業務需求規劃網段。本文以建立華東1(杭州)地區的VPC和交換器為例。建立完畢後返回ECS購買頁,重新整理並選擇VPC及交換器。
說明建立VPC時,可同時建立交換器。
選擇規格 & 鏡像
選擇執行個體的規格及鏡像,鏡像為執行個體確定安裝的作業系統及版本。本文選擇的執行個體規格為
ecs.e-c1m1.large
,在滿足測試需求的同時,價格較為實惠。鏡像為公用鏡像Alibaba Cloud Linux 3.2104 LTS 64位
。
選擇儲存
為ECS執行個體選擇系統硬碟,並按需選擇資料盤。本文實現簡單Web系統搭建,只需要系統硬碟儲存作業系統,無需資料盤。
綁定公網IP
本執行個體需要支援公網訪問。為了簡化操作,本文選擇直接為執行個體分配公網IP。您也可以在建立執行個體後,為執行個體綁定Elastic IP Address,具體操作,請參見將EIP綁定至ECS執行個體。
說明若未綁定公網IP,將無法使用SSH或RDP通過公網直接存取執行個體,也無法通過公網驗證執行個體中Web服務的搭建。
本文選擇按使用流量的頻寬計費模式。此模式只需為所消耗的公網流量付費。更多資訊,請參見公網頻寬計費。
建立安全性群組
為執行個體建立安全性群組。安全性群組是一種虛擬網路防火牆,能夠控制ECS執行個體的出入流量。建立時,需要設定允許存取以下指定連接埠,便於後續訪問ECS執行個體。
連接埠範圍:SSH(22)、RDP(3389)、HTTP(80)、HTTPS(443)。
說明連接埠範圍處選中的是ECS執行個體上啟動並執行應用需開放的連接埠。
此處建立的安全性群組預設設定0.0.0.0/0作為源的規則。0.0.0.0/0表示允許全網段裝置訪問指定的連接埠,如果您知道請求端的IP地址,建議後續設定為具體的IP範圍。具體操作,請參見修改安全性群組規則。
建立金鑰組
金鑰組可作為登入時證明個人身份的安全憑證,建立完成後,必須下載私密金鑰,以供後續串連ECS執行個體時使用。建立完畢後返回ECS購買頁,重新整理並選擇金鑰組。
root
具有作業系統的最高許可權,使用root
作為登入名稱可能會導致安全風險,建議您選擇ecs-user
作為登入名稱。說明建立金鑰組後,私密金鑰會自動下載,請您關注瀏覽器的下載記錄,儲存
.pem
格式的私密金鑰檔案。
建立並查看ECS執行個體
建立或選擇好ECS執行個體所需的基礎資源後,單擊確認下單。在提示成功的對話方塊中,單擊管理主控台,即可在控制台查看到建立好的ECS執行個體。請您儲存以下資料,以便在後續操作中使用。
執行個體ID:便於在執行個體列表中查詢到該執行個體。
地區:便於在執行個體列表中查詢到該執行個體。
公網IP地址:便於在後續使用ECS執行個體時,做Web服務的部署結果驗證。
操作二:串連ECS執行個體
在Elastic Compute Service控制台的執行個體列表頁面,根據地區、執行個體ID找到建立好的ECS執行個體,單擊操作列下的遠端連線。
在遠端連線對話方塊中,單擊通過Workbench遠端連線對應的立即登入。
在登入執行個體對話方塊中,選擇認證方式為SSH密鑰認證,使用者名稱為
ecs-user
,輸入或上傳建立金鑰組時下載的私密金鑰檔案,單擊確定,即可登入ECS執行個體。說明私密金鑰檔案在建立金鑰組時自動下載到本地,請您關注瀏覽器的下載記錄,尋找
.pem
格式的私密金鑰檔案。顯示如下頁面後,即說明您已成功登入ECS執行個體。
一、配置使用者權限
為了確保部署完成後不會因為操作未授權而導致檔案上傳到OSS失敗,建議您先按照以下步驟建立RAM使用者並配置相應的許可權。
操作一:在存取控制建立RAM使用者
首先,建立一個RAM使用者,並擷取對應的存取金鑰,作為商務服務器的應用程式的長期身份憑證。
使用雲帳號或帳號管理員登入RAM控制台。
在左側導覽列,選擇身份管理 > 使用者。
單擊建立使用者。
輸入登入名稱稱和顯示名稱。
在訪問方式地區下,選擇使用永久 AccessKey 訪問,然後單擊確定。
RAM使用者的AccessKey Secret只在建立時顯示,後續不支援查看,請妥善保管。
單擊操作下的複製,儲存調用密鑰(AccessKey ID和AccessKey Secret)。
操作二:在存取控制為RAM使用者授予調用AssumeRole介面的許可權
建立RAM使用者後,需要授予RAM使用者調用STS服務的AssumeRole介面的許可權,使其可以通過扮演RAM角色來擷取臨時身份憑證。
在左側導覽列,選擇身份管理 > 使用者。
在使用者頁面,找到目標RAM使用者,然後單擊RAM使用者右側的添加許可權。
在新增授權頁面,選擇AliyunSTSAssumeRoleAccess系統策略。
說明授予RAM使用者調用STS服務AssumeRole介面的固定許可權是AliyunSTSAssumeRoleAccess,與後續擷取臨時訪問憑證以及通過臨時訪問憑證發起OSS請求要求的權限無關。
單擊確認新增授權。
操作三:在存取控制建立RAM角色
為當前雲帳號建立一個RAM角色,並擷取對應的角色的ARN(Aliyun Resource Name,阿里雲資源名稱),用於RAM使用者之後進行扮演。
在左側導覽列,選擇身份管理 > 角色。
單擊建立角色,可信實體類型選擇雲帳號。
選擇當前雲帳號,單擊確定。
填寫角色名稱,單擊確定。
在RAM角色管理頁面,單擊複製,儲存角色的ARN。
操作四:在存取控制建立上傳檔案的權限原則
按照最小授權原則,為RAM角色建立一個自訂權限原則,限制只能向指定OSS的儲存空間進行上傳操作。
在左側導覽列,選擇許可權管理 > 權限原則。
單擊建立權限原則。
在建立權限原則頁面,單擊指令碼編輯,將以下指令碼中的
<Bucket名稱>
替換為準備工作中建立的Bucket名稱web-direct-upload
。{ "Version": "1", "Statement": [ { "Effect": "Allow", "Action": "oss:PutObject", "Resource": "acs:oss:*:*:<Bucket名稱>/*" } ] }
策略配置完成後,單擊繼續編輯基本資料。
在基本資料地區,填寫策略名稱稱,然後單擊確定。
操作五:在存取控制為RAM角色授予許可權
為RAM角色授予建立的自訂許可權,以便該RAM角色被扮演時能擷取所需的許可權。
在左側導覽列,選擇身份管理 > 角色。
在角色頁面,找到目標RAM角色,然後單擊RAM角色右側的新增授權。
在新增授權頁面下,選擇自訂策略,選擇已建立的自訂權限原則。
單擊確定。
二、服務端擷取臨時訪問憑證並計算簽名
建議您先將敏感資訊(如accessKeyId
、accessKeySecret
和roleArn
)配置到環境變數,從而避免在代碼裡顯式地配置,降低泄露風險。
您可以僅在當前會話中使用該環境變數,可以參照以下步驟添加臨時環境變數。
Linux系統
執行以下命令。
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"
執行以下命令,驗證該環境變數是否生效。
echo $OSS_ACCESS_KEY_ID echo $OSS_ACCESS_KEY_SECRET echo $OSS_STS_ROLE_ARN
macOS系統
執行以下命令。
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"
執行以下命令,驗證該環境變數是否生效。
echo $OSS_ACCESS_KEY_ID echo $OSS_ACCESS_KEY_SECRET echo $OSS_STS_ROLE_ARN
Windows系統
在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"
開啟一個新的CMD視窗。
在新的CMD視窗運行以下命令,檢查環境變數是否生效。
echo $OSS_ACCESS_KEY_ID echo $OSS_ACCESS_KEY_SECRET echo $OSS_STS_ROLE_ARN
您可以參考以下代碼,在服務端進行POST簽名版本4(推薦)的計算工作。關於policy表單域詳細配置資訊,請參見policy表單域。
Java
在Maven專案中,匯入以下依賴。
<!-- 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>
您可以參考如下代碼來完成服務端擷取臨時訪問憑證並計算POST簽名:
package com.aliyun.oss.web;
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 {
//OSS基礎資訊 替換為實際的 bucket 名稱、 region-id、host。
String bucket = "examplebucket";
String region = "cn-hangzhou";
String host = "http://examplebucket.oss-cn-hangzhou.aliyuncs.com";
// 設定上傳回調URL(即回調伺服器位址),必須為公網地址。用於處理應用伺服器與OSS之間的通訊,OSS會在檔案上傳完成後,把檔案上傳資訊通過此回調URL發送給應用伺服器。
//限定上傳到OSS的檔案首碼。
String upload_dir = "dir";
//指定到期時間,單位為秒。
Long expire_time = 3600L;
/**
* 通過指定有效時間長度(秒)產生到期時間。
* @param seconds 有效時間長度(秒)。
* @return ISO8601 時間字串,如:"2014-12-01T12:00:00.000Z"。
*/
public static String generateExpiration(long seconds) {
// 擷取目前時間戳(以秒為單位)
long now = Instant.now().getEpochSecond();
// 計算到期時間的時間戳記
long expirationTime = now + seconds;
// 將時間戳記轉換為Instant對象,並格式化為ISO8601格式
Instant instant = Instant.ofEpochSecond(expirationTime);
// 定義時區
ZoneId zone = ZoneId.systemDefault(); // 使用系統預設時區
// 將 Instant 轉換為 ZonedDateTime
ZonedDateTime zonedDateTime = instant.atZone(zone);
// 定義日期時間格式,例如2023-12-03T13:00:00.000Z
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
// 格式化日期時間
String formattedDate = zonedDateTime.format(formatter);
// 輸出結果
return formattedDate;
}
//初始化STS Client
public static com.aliyun.sts20150401.Client createStsClient() throws Exception {
// 工程代碼泄露可能會導致 AccessKey 泄露,並威脅帳號下所有資源的安全性。以下程式碼範例僅供參考。
// 建議使用更安全的 STS 方式。
com.aliyun.teaopenapi.models.Config config = new com.aliyun.teaopenapi.models.Config()
// 必填,請確保代碼運行環境設定了環境變數 OSS_ACCESS_KEY_ID。
.setAccessKeyId(System.getenv("OSS_ACCESS_KEY_ID"))
// 必填,請確保代碼運行環境設定了環境變數 OSS_ACCESS_KEY_SECRET。
.setAccessKeySecret(System.getenv("OSS_ACCESS_KEY_SECRET"));
// Endpoint 請參考 https://api.aliyun.com/product/Sts
config.endpoint = "sts.cn-hangzhou.aliyuncs.com";
return new com.aliyun.sts20150401.Client(config);
}
//擷取STS臨時憑證
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()
// 必填,請確保代碼運行環境設定了環境變數 OSS_STS_ROLE_ARN
.setRoleArn(System.getenv("OSS_STS_ROLE_ARN"))
.setRoleSessionName("yourRoleSessionName");// 自訂會話名稱
com.aliyun.teautil.models.RuntimeOptions runtime = new com.aliyun.teautil.models.RuntimeOptions();
try {
// 複製代碼運行請自行列印 API 的傳回值
AssumeRoleResponse response = client.assumeRoleWithOptions(assumeRoleRequest, runtime);
// credentials裡包含了後續要用到的AccessKeyId、AccessKeySecret和SecurityToken。
return response.body.credentials;
} catch (TeaException error) {
// 此處僅做列印展示,請謹慎對待異常處理,在工程專案中切勿直接忽略異常。
// 錯誤 message
System.out.println(error.getMessage());
// 診斷地址
System.out.println(error.getData().get("Recommend"));
com.aliyun.teautil.Common.assertAsString(error.message);
} catch (Exception _error) {
TeaException error = new TeaException(_error.getMessage(), _error);
// 此處僅做列印展示,請謹慎對待異常處理,在工程專案中切勿直接忽略異常。
// 錯誤 message
System.out.println(error.getMessage());
// 診斷地址
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;
//擷取x-oss-credential裡的date,當前日期,格式為yyyyMMdd
LocalDate today = LocalDate.now();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd");
String date = today.format(formatter);
//擷取x-oss-date
ZonedDateTime now = ZonedDateTime.now().withZoneSameInstant(java.time.ZoneOffset.UTC);
DateTimeFormatter formatter2 = DateTimeFormatter.ofPattern("yyyyMMdd'T'HHmmss'Z'");
String x_oss_date = now.format(formatter2);
// 步驟1:建立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); // 替換為實際的 access key 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);
// 步驟2:構造待簽名字串(StringToSign)。
String stringToSign = new String(Base64.encodeBase64(jsonPolicy.getBytes()));
// System.out.println("stringToSign: " + stringToSign);
// 步驟3:計算SigningKey。
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));
// 步驟4:計算Signature。
byte[] result = hmacsha256(signingKey, stringToSign);
String signature = BinaryUtil.toHex(result);
// System.out.println("signature:" + signature);
Map<String, String> response = new HashMap<>();
// 將資料添加到 map 中
response.put("version", "OSS4-HMAC-SHA256");
// 這裡是易錯點,不能直接傳policy,需要做一下Base64編碼
response.put("policy", stringToSign);
response.put("x_oss_credential", x_oss_credential);
response.put("x_oss_date", x_oss_date);
response.put("signature", signature);
response.put("security_token", securitytoken);
response.put("dir", upload_dir);
response.put("host", host);
// 返回帶有狀態代碼 200 (OK) 的 ResponseEntity,返回給Web端,進行PostObject操作
return ResponseEntity.ok(response);
}
public static byte[] hmacsha256(byte[] key, String data) {
try {
// 初始化HMAC密鑰規格,指定演算法為HMAC-SHA256並使用提供的密鑰。
SecretKeySpec secretKeySpec = new SecretKeySpec(key, "HmacSHA256");
// 擷取Mac執行個體,並通過getInstance方法指定使用HMAC-SHA256演算法。
Mac mac = Mac.getInstance("HmacSHA256");
// 使用密鑰初始化Mac對象。
mac.init(secretKeySpec);
// 執行HMAC計算,通過doFinal方法接收需要計算的資料並返回計算結果的數組。
byte[] hmacBytes = mac.doFinal(data.getBytes());
return hmacBytes;
} catch (Exception e) {
throw new RuntimeException("Failed to calculate HMAC-SHA256", e);
}
}
}
Python
執行以下命令安裝依賴。
pip install flask
pip install alibabacloud_tea_openapi alibabacloud_sts20150401 alibabacloud_credentials
請參考如下代碼來完成服務端擷取臨時訪問憑證STStoken並構建上傳策略以計算POST簽名。
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__)
# 配置環境變數 OSS_ACCESS_KEY_ID, OSS_ACCESS_KEY_ID, OSS_STS_ROLE_ARN。
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')
# 自訂會話名稱
role_session_name = 'yourRoleSessionName'
# 替換為實際的bucket名稱、region_id、host
bucket = 'examplebucket'
region_id = 'cn-hangzhou'
host = 'http://examplebucket.oss-cn-hangzhou.aliyuncs.com'
# 指定到期時間,單位為秒
expire_time = 3600
# 指定上傳到OSS的檔案首碼。
upload_dir = 'dir'
def hmacsha256(key, data):
"""
計算HMAC-SHA256雜湊值的函數
:param key: 用於計算雜湊的密鑰,位元組類型
:param data: 要進行雜湊計算的資料,字串類型
:return: 計算得到的HMAC-SHA256雜湊值,位元組類型
"""
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():
# 初始化配置,直接傳遞憑據
config = Config(
region_id=region_id,
access_key_id=access_key_id,
access_key_secret=access_key_secret
)
# 建立 STS 用戶端並擷取臨時憑證
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()
# 使用 STS 返回的臨時憑據
temp_access_key_id = token_data['AccessKeyId']
temp_access_key_secret = token_data['AccessKeySecret']
security_token = token_data['SecurityToken']
now = int(time.time())
# 將時間戳記轉換為datetime對象
dt_obj = datetime.datetime.utcfromtimestamp(now)
# 在目前時間增加3小時,設定為請求的到期時間
dt_obj_plus_3h = dt_obj + datetime.timedelta(hours=3)
# 請求時間
dt_obj_1 = dt_obj.strftime('%Y%m%dT%H%M%S') + 'Z'
# 請求日期
dt_obj_2 = dt_obj.strftime('%Y%m%d')
# 請求到期時間
expiration_time = dt_obj_plus_3h.strftime('%Y-%m-%dT%H:%M:%S.000Z')
# 構建 Policy 並產生簽名
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()
# 步驟2:構造待簽名字串(StringToSign)
stringToSign = base64.b64encode(policy_str.encode()).decode()
# 步驟3:計算SigningKey
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")
# 步驟4:計算Signature
result = hmacsha256(signingKey, stringToSign)
signature = result.hex()
# 組織返回資料
response_data = {
'policy': stringToSign, #表單域
'x_oss_signature_version': "OSS4-HMAC-SHA256", #指定簽名的版本和演算法,固定值為OSS4-HMAC-SHA256
'x_oss_credential': f"{temp_access_key_id}/{dt_obj_2}/cn-hangzhou/oss/aliyun_v4_request", #指明衍生金鑰的參數集
'x_oss_date': dt_obj_1, #請求的時間
'signature': signature, #簽名認證描述資訊
'host': host,
'dir': upload_dir,
'security_token': security_token #安全性權杖
}
return jsonify(response_data)
if __name__ == "__main__":
app.run(host="127.0.0.1", port=8000) # 如果需要監聽其他地址如0.0.0.0,需要您自行在服務端添加認證機制
Node.js
執行以下命令安裝依賴。
npm install ali-oss
npm install @alicloud/credentials
npm install express
請參考如下代碼來完成服務端擷取臨時訪問憑證STStoken並構建上傳策略以計算POST簽名。
const express = require('express');
const OSS = require('ali-oss');
const { STS } = require('ali-oss');
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 || 8000; // 服務要求連接埠號碼
// 設定靜態檔案目錄
app.use(express.static('templates'));
const GenerateSignature = async () => {
// 初始化STS用戶端
let sts = new STS({
accessKeyId: process.env.OSS_ACCESS_KEY_ID, // 從環境變數中擷取RAM使用者的AccessKey ID
accessKeySecret: process.env.OSS_ACCESS_KEY_SECRET // 從環境變數中擷取RAM使用者的AccessKey Secret
});
// 調用assumeRole介面擷取STS臨時訪問憑證
const result = await sts.assumeRole(process.env.OSS_STS_ROLE_ARN, '', '3600', 'yourRoleSessionName'); // 從環境變數中擷取RAM角色ARN,並設定臨時訪問憑證有效期間為3600秒,角色會話名稱為yourRoleSessionName可自訂
// 提取臨時訪問憑證中的AccessKeyId、AccessKeySecret和SecurityToken
const accessKeyId = result.credentials.AccessKeyId;
const accessKeySecret = result.credentials.AccessKeySecret;
const securityToken = result.credentials.SecurityToken;
// 初始化OSS Client
const client = new OSS({
bucket: 'examplebucket', // 請替換為目標Bucket名稱
region: 'cn-hangzhou', // 請替換為標Bucket所在地區
accessKeyId,
accessKeySecret,
stsToken: securityToken,
refreshSTSTokenInterval: 0,
refreshSTSToken: async () => {
const { accessKeyId, accessKeySecret, securityToken } = await client.getCredential();
return { accessKeyId, accessKeySecret, stsToken: securityToken };
},
});
// 建立表單資料Map
const formData = new Map();
// 設定簽名到期時間為目前時間往後推10分鐘
const date = new Date();
const expirationDate = new Date(date);
expirationDate.setMinutes(date.getMinutes() + 10);
// 格式化日期為符合ISO 8601標準的UTC時間字串格式
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);
// 產生x-oss-credential並設定表單資料
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');
// 建立policy
// 樣本policy表單域只列舉必要欄位
const policy = {
expiration: expirationDate.toISOString(),
conditions: [
{ 'bucket': 'examplebucket'},
{ 'x-oss-credential': credential },
{ 'x-oss-signature-version': 'OSS4-HMAC-SHA256' },
{ 'x-oss-date': formattedDate },
],
};
// 如果存在STS Token,添加到策略和表單資料中
if (client.options.stsToken) {
policy.conditions.push({ 'x-oss-security-token': client.options.stsToken });
formData.set('security_token', client.options.stsToken);
}
// 產生簽名並設定表單資料
const signature = client.signPostObjectPolicyV4(policy, date);
formData.set('policy', Buffer.from(policy2Str(policy), 'utf8').toString('base64'));
formData.set('signature', signature);
// 返回表單資料
return {
host: `http://${client.options.bucket}.oss-${client.options.region}.aliyuncs.com`,
policy: Buffer.from(policy2Str(policy), 'utf8').toString('base64'),
x_oss_signature_version: 'OSS4-HMAC-SHA256',
x_oss_credential: credential,
x_oss_date: formattedDate,
signature: signature,
dir: 'user-dir', // 指定上傳到OSS的檔案首碼
security_token: client.options.stsToken
};
};
app.get('/get_post_signature_for_oss_upload', async (req, res) => {
try {
const result = await GenerateSignature();
res.json(result); // 返回產生的簽名資料
} 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://127.0.0.1:${PORT}`); // 如果需要監聽其他地址如0.0.0.0,需要您自行在服務端添加認證機制
});
Go
執行以下命令安裝依賴。
go get -u github.com/aliyun/credentials-go
go mod tidy
請參考如下代碼來完成服務端擷取臨時訪問憑證STStoken並構建上傳策略以計算POST簽名。
package main
import (
"crypto/hmac"
"crypto/sha256"
"encoding/base64"
"encoding/hex"
"encoding/json"
"fmt"
"io"
"hash"
"log"
"net/http"
"os"
"time"
"github.com/aliyun/credentials-go/credentials"
)
// 定義全域變數
var (
region string
bucketName string
product = "oss"
)
// PolicyToken 結構體用於儲存產生的表單資料
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"`
Host string `json:"host"`
Dir string `json:"dir"`
}
func main() {
// 定義預設的IP和連接埠字串
strIPPort := ":8000"
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 test1.go ")
fmt.Println("Usage : go run test1.go ip port ")
fmt.Println("")
os.Exit(0)
}
// 列印伺服器啟動並執行地址和連接埠
fmt.Printf("server is running on %s \n", strIPPort)
// 註冊處理根路徑請求的函數
http.HandleFunc("/", handlerRequest)
// 註冊處理擷取簽章要求的函數
http.HandleFunc("/get_post_signature_for_oss_upload", handleGetPostSignature)
// 啟動HTTP伺服器
err := http.ListenAndServe(strIPPort, nil)
if err != nil {
strError := fmt.Sprintf("http.ListenAndServe failed : %s \n", err.Error())
panic(strError)
}
}
// handlerRequest 函數處理根路徑請求
func handlerRequest(w http.ResponseWriter, r *http.Request) {
if r.Method == "GET" {
http.ServeFile(w, r, "templates/index.html")
return
}
http.NotFound(w, r)
}
// handleGetPostSignature 函數處理擷取簽章要求
func handleGetPostSignature(w http.ResponseWriter, r *http.Request) {
if r.Method == "GET" {
response := getPolicyToken()
w.Header().Set("Content-Type", "application/json")
w.Header().Set("Access-Control-Allow-Origin", "*") // 允許跨域
w.Write([]byte(response))
return
}
http.NotFound(w, r)
}
// getPolicyToken 函數產生 OSS 上傳所需的簽名和憑證
func getPolicyToken() string {
// 設定bucket所處地區
region = "cn-hangzhou"
// 替換為您的bucket名稱
bucketName = "examplebucket"
// 設定 OSS 上傳地址
host := fmt.Sprintf("https://%s.oss-%s.aliyuncs.com", bucketName, region)
// 設定上傳目錄
dir := "user-dir"
config := new(credentials.Config).
SetType("ram_role_arn").
SetAccessKeyId(os.Getenv("OSS_ACCESS_KEY_ID")).
SetAccessKeySecret(os.Getenv("OSS_ACCESS_KEY_SECRET")).
SetRoleArn("OSS_STS_ROLE_ARN").
SetRoleSessionName("Role_Session_Name").
SetPolicy("").
SetRoleSessionExpiration(3600)
// 根據配置建立憑證提供器
provider, err := credentials.NewCredential(config)
if err != nil {
log.Fatalf("NewCredential fail, err:%v", err)
}
// 從憑證提供器擷取憑證
cred, err := provider.GetCredential()
if err != nil {
log.Fatalf("GetCredential fail, err:%v", err)
}
// 構建policy
utcTime := time.Now().UTC()
date := utcTime.Format("20060102")
expiration := utcTime.Add(1 * time.Hour)
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},
},
}
// 將policy轉換為 JSON 格式
policy, err := json.Marshal(policyMap)
if err != nil {
log.Fatalf("json.Marshal fail, err:%v", err)
}
// 構造待簽名字串(StringToSign)
stringToSign := base64.StdEncoding.EncodeToString([]byte(policy))
hmacHash := func() hash.Hash { return sha256.New() }
// 構建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)
// 產生簽名
h := hmac.New(hmacHash, h4Key)
io.WriteString(h, stringToSign)
signature := hex.EncodeToString(h.Sum(nil))
// 構建返回給前端的表單
policyToken := PolicyToken{
Policy: stringToSign,
SecurityToken: *cred.SecurityToken,
SignatureVersion: "OSS4-HMAC-SHA256",
Credential: fmt.Sprintf("%v/%v/%v/%v/aliyun_v4_request", *cred.AccessKeyId, date, region, product),
Date: utcTime.UTC().Format("20060102T150405Z"),
Signature: signature,
Host: host, // 返回 OSS 上傳地址
Dir: dir, // 返回上傳目錄
}
response, err := json.Marshal(policyToken)
if err != nil {
fmt.Println("json err:", err)
}
return string(response)
}
PHP
執行以下命令安裝依賴。
composer install
請參考如下代碼來完成服務端擷取臨時訪問憑證STStoken並構建上傳策略以計算POST簽名。
<?php
// 引入阿里雲SDK
require_once __DIR__ . '/vendor/autoload.php';
use AlibabaCloud\Client\AlibabaCloud;
use AlibabaCloud\Client\Exception\ClientException;
use AlibabaCloud\Client\Exception\ServerException;
use AlibabaCloud\Sts\Sts;
// 禁用錯誤顯示
ini_set('display_errors', '0');
$bucket = 'examplebucket'; // 替換為您的Bucket名稱
$region_id = 'cn-hangzhou'; // 替換為您的Bucket所在地區
$host = 'http://examplebucket.oss-cn-hangzhou.aliyuncs.com'; // 替換為您的Bucket網域名稱
$expire_time = 3600; // 到期時間,單位為秒
$upload_dir = 'user-dir'; // 上傳檔案的首碼
// 計算HMAC-SHA256
function hmacsha256($key, $data) {
return hash_hmac('sha256', $data, $key, true);
}
// 處理擷取POST簽名的請求
if ($_SERVER['REQUEST_METHOD'] === 'GET' && $_SERVER['REQUEST_URI'] === '/get_post_signature_for_oss_upload') {
AlibabaCloud::accessKeyClient(getenv('OSS_ACCESS_KEY_ID'), getenv('OSS_ACCESS_KEY_SECRET'))
->regionId('cn-hangzhou')
->asDefaultClient();
// 建立STS請求。
$request = Sts::v20150401()->assumeRole();
// 發起STS請求並擷取結果。
// 將<YOUR_ROLE_SESSION_NAME>設定為自訂的會話名稱,例如oss-role-session。
// 將<YOUR_ROLE_ARN>替換為擁有上傳檔案到指定OSS Bucket許可權的RAM角色的ARN。
$result = $request
->withRoleSessionName('oss-role-session')
->withDurationSeconds(3600)
->withRoleArn(getenv('OSS_STS_ROLE_ARN'))
->request();
// 擷取STS請求結果中的憑證資訊。
$tokenData = $result->get('Credentials');
// 構建返回的JSON資料。
$tempAccessKeyId = $tokenData['AccessKeyId'];
$tempAccessKeySecret = $tokenData['AccessKeySecret'];
$securityToken = $tokenData['SecurityToken'];
$now = time();
$dtObj = gmdate('Ymd\THis\Z', $now);
$dtObj1 = gmdate('Ymd', $now);
$dtObjPlus3h = gmdate('Y-m-d\TH:i:s.u\Z', strtotime('+3 hours', $now));
// 構建Policy
$policy = [
"expiration" => $dtObjPlus3h,
"conditions" => [
["x-oss-signature-version" => "OSS4-HMAC-SHA256"],
["x-oss-credential" => "{$tempAccessKeyId}/{$dtObj1}/cn-hangzhou/oss/aliyun_v4_request"],
["x-oss-security-token" => $securityToken],
["x-oss-date" => $dtObj],
]
];
$policyStr = json_encode($policy);
// 構造待簽名字串
$stringToSign = base64_encode($policyStr);
// 計算SigningKey
$dateKey = hmacsha256(('aliyun_v4' . $tempAccessKeySecret), $dtObj1);
$dateRegionKey = hmacsha256($dateKey, 'cn-hangzhou');
$dateRegionServiceKey = hmacsha256($dateRegionKey, 'oss');
$signingKey = hmacsha256($dateRegionServiceKey, 'aliyun_v4_request');
// 計算Signature
$result = hmacsha256($signingKey, $stringToSign);
$signature = bin2hex($result);
// 返回簽名資料
$responseData = [
'policy' => $stringToSign,
'x_oss_signature_version' => "OSS4-HMAC-SHA256",
'x_oss_credential' => "{$tempAccessKeyId}/{$dtObj1}/cn-hangzhou/oss/aliyun_v4_request",
'x_oss_date' => $dtObj,
'signature' => $signature,
'host' => $host,
'dir' => $upload_dir,
'security_token' => $securityToken
];
header('Content-Type: application/json');
echo json_encode($responseData);
exit;
}
// 首頁路由
if ($_SERVER['REQUEST_METHOD'] === 'GET' && $_SERVER['REQUEST_URI'] === '/') {
echo file_get_contents(__DIR__ . '/public/index.html');
exit;
}
// 其他路由
http_response_code(404);
echo json_encode(['message' => 'Not Found']);
exit;
?>
步驟三:配置Web端
Web端構造並提交表單上傳請求
當Web端從服務端接收到所有必需的資訊後,就可以構建HTML表單請求了。此請求將直接與OSS服務進行通訊,從而實現檔案的上傳。
Web端接收到的響應樣本
商務服務器向Web端返回STS Token和上傳策略。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>服務端產生簽名上傳檔案到OSS</title>
</head>
<body>
<div class="container">
<form>
<div class="mb-3">
<label for="file" class="form-label">選擇檔案:</label>
<input type="file" class="form-control" id="file" name="file" required />
</div>
<button type="submit" class="btn btn-primary">上傳</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('請選擇一個檔案再上傳。');
return;
}
const filename = file.name;
fetch("/get_post_signature_for_oss_upload", { method: "GET" })
.then((response) => {
if (!response.ok) {
throw new Error("擷取簽名失敗");
}
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); // 檔案名稱
formData.append("x-oss-security-token", data.security_token);
formData.append("file", file); // file 必須為最後一個表單域
return fetch(data.host, {
method: "POST",
body: formData
});
})
.then((response) => {
if (response.ok) {
console.log("上傳成功");
alert("檔案已上傳");
} else {
console.log("上傳失敗", response);
alert("上傳失敗,請稍後再試");
}
})
.catch((error) => {
console.error("發生錯誤:", error);
});
});
});
</script>
</body>
</html>
{
"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"
}
Body中的各欄位說明如下:
欄位 | 描述 |
dir | 限制上傳的檔案首碼。 |
host | Bucket網域名稱。 |
policy | 使用者表單上傳的策略(Policy),詳情請參見Post Policy。 |
security_token | 安全性權杖。 |
signature | 對Policy簽名後的字串。詳情請參見Post Signature。 |
x_oss_credential | 指明衍生金鑰的參數集。 |
x_oss_date | 請求的時間,其格式遵循ISO 8601日期和時間標準,例如 |
x_oss_signature_version | 指定簽名的版本和演算法,固定值為OSS4-HMAC-SHA256。 |
表單請求中包含檔案內容和伺服器返回的參數。
通過這個請求,Web端可以直接與阿里雲的OSS進行通訊,完成檔案上傳。
除file表單域外,包含key在內的其他所有表單域的大小均不能超過8 KB。
Web端上傳預設同名覆蓋,如果您不希望覆蓋同名檔案,可以在上傳請求的header中攜帶參數x-oss-forbid-overwrite,並指定其值為true。當您上傳的檔案在OSS中存在同名檔案時,該檔案會上傳失敗,並返回FileAlreadyExists錯誤。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>服務端產生簽名上傳檔案到OSS</title>
</head>
<body>
<div class="container">
<form>
<div class="mb-3">
<label for="file" class="form-label">選擇檔案:</label>
<input type="file" class="form-control" id="file" name="file" required />
</div>
<button type="submit" class="btn btn-primary">上傳</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('請選擇一個檔案再上傳。');
return;
}
const filename = file.name;
fetch("/get_post_signature_for_oss_upload", { method: "GET" })
.then((response) => {
if (!response.ok) {
throw new Error("擷取簽名失敗");
}
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); // 檔案名稱
formData.append("x-oss-security-token", data.security_token);
formData.append("file", file); // file 必須為最後一個表單域
return fetch(data.host, {
method: "POST",
body: formData
});
})
.then((response) => {
if (response.ok) {
console.log("上傳成功");
alert("檔案已上傳");
} else {
console.log("上傳失敗", response);
alert("上傳失敗,請稍後再試");
}
})
.catch((error) => {
console.error("發生錯誤:", error);
});
});
});
</script>
</body>
</html>
HTML表單包含一個檔案輸入框和一個提交按鈕,使用者可以選擇要上傳的檔案並提交表單。
當表單提交時,JavaScript代碼會阻止預設的表單提交行為,然後通過AJAX請求從伺服器擷取上傳所需的簽名資訊。
擷取到簽名資訊後,構造一個
FormData
對象,包含所有必要的表單欄位。通過
fetch
方法發送POST請求到OSS服務的URL,完成檔案上傳。
如果上傳成功,顯示“檔案已上傳”的提示;如果上傳失敗,顯示相應的錯誤資訊。
結果驗證
以上步驟部署完成後,您可以訪問伺服器位址,體驗Web端簽名直傳功能。
通過瀏覽器訪問服務端地址,然後點擊上傳按鈕選擇上傳的檔案。效果樣本如下:
在Bucket列表頁面,選擇您之前建立的用來存放使用者上傳檔案的Bucket並開啟,您可以在上傳列表中看到您通過Web端上傳的檔案。
建議配置
限制檔案上傳的許可權和約束條件
您可以根據實際需求修改代碼中的policy ,以設定通過HTML表單上傳檔案到OSS時的許可權限制和約束條件。policy表單域採用JSON格式定義,通過設定不同的參數來限制上傳操作,確保上傳過程的安全性和合規性,例如允許上傳的Bucket名稱、Object首碼、有效期間、允許的HTTP方法、上傳內容的大小限制、內容類型限制等。
{
"expiration": "2023-12-03T13:00:00.000Z",
"conditions": [
{"bucket": "examplebucket"},
{"x-oss-signature-version": "OSS4-HMAC-SHA256"},
{"x-oss-credential": "AKIDEXAMPLE/20231203/cn-hangzhou/oss/aliyun_v4_request"},
{"x-oss-security-token": "CAIS******"},
{"x-oss-date": "20231203T121212Z"},
["content-length-range", 1, 10],
["eq", "$success_action_status", "201"],
["starts-with", "$key", "user/eric/"],
["in", "$content-type", ["image/jpg", "image/png"]],
["not-in", "$cache-control", ["no-cache"]]
]
}
policy詳細說明如下:
欄位 | 類型 | 是否必選 | 描述 | Conditions匹配方式 |
content-length-range | 字串 | 否 | 上傳Object的最小和最大允許大小,單位為位元組。 | content-length-range |
success_action_status | 字串 | 否 | 上傳成功後的返回狀態代碼。 | eq、starts-with、in和not-in |
key | 字串 | 否 | 上傳的Object名稱。 | eq、starts-with、in和not-in |
content-type | 字串 | 否 | 限制上傳的檔案類型。 | eq、starts-with、in和not-in |
cache-control | 字串 | 否 | 指定Object的緩衝行為。 | eq、starts-with、in和not-in |
關於Policy參數的更多說明請參見Post Policy。
服務端簽名直傳並設定上傳回調
如果您需要擷取更多關於使用者上傳檔案的資訊,例如檔案名稱、圖片大小等,請使用上傳回調方案。通過設定上傳回調,您可以在使用者上傳檔案後,自動接收到相關檔案資訊。關於如何佈建服務端簽名直傳並設定上傳回調,請參見伺服器端簽名直傳並設定上傳回調。
配置CORS規則時將來源設為伺服器位址
在之前的操作步驟中,為了簡化流程,將允許的跨域請求來源設定為萬用字元 *
。然而,出於安全考慮,建議您對來源進行更嚴格的限制。為此,您可以將建立的OSS Bucket的跨域資源共用來源參數設定為您商務服務器的具體地址。這樣一來,唯有來自您指定伺服器的請求才能被授權執行跨網域作業,有效增強了系統的安全性。
參數 | 樣本值 |
來源 |
|
允許Methods | POST、PUT、GET |
允許Headers | * |
清理資源
在本方案中,您建立了1台ECS執行個體、1個OSS Bucket、1個RAM使用者和1個RAM角色。測試完方案後,您可以參考以下規則處理對應產品的資源,避免繼續產生費用或產生安全風險。
釋放ECS執行個體
如果您不再需要這台執行個體,可以將其釋放。釋放後,執行個體停止計費,資料不可恢複。具體操作如下:
返回Elastic Compute Service控制台的執行個體列表頁面,根據地區、執行個體ID找到目標ECS執行個體,單擊操作列下的
。
選擇釋放。
確認執行個體無誤後,選擇立即釋放,單擊下一步。
確認即將釋放的關聯資源,並瞭解相關資料風險後,單擊確認,即可完成ECS執行個體的釋放。
系統硬碟、分配的公網IP將隨執行個體釋放。
安全性群組、交換器和VPC不會隨執行個體釋放,但它們均為免費資源,您可根據實際的業務需要選擇性刪除。
Elastic IP Address不會隨執行個體釋放,且不是免費資源,您可根據實際的業務需要選擇性刪除。
刪除Bucket
登入OSS管理主控台。
單擊Bucket 列表,然後單擊目標Bucket名稱。
刪除Bucket的所有檔案(Object)。
在左側導覽列,單擊移除Bucket,然後按照頁面指引完成刪除操作。
刪除RAM使用者
使用Resource Access Management員登入RAM控制台。
在左側導覽列,選擇身份管理 > 使用者。
在使用者頁面,單擊目標RAM使用者操作列的刪除。
您也可以選中多個RAM使用者,然後單擊使用者列表下方的刪除使用者,批量將多個RAM使用者移入資源回收筒。
在刪除使用者對話方塊,仔細閱讀刪除影響,然後輸入目標RAM使用者名稱稱,最後單擊移入資源回收筒。
刪除RAM角色
使用Resource Access Management員登入RAM控制台。
在左側導覽列,選擇身份管理 > 角色。
在角色頁面,單擊目標RAM角色操作列的刪除角色。
在刪除角色對話方塊,輸入RAM角色名稱,然後單擊刪除角色。
說明如果RAM角色被授予了權限原則,刪除角色時,會同時解除授權。
常見問題
是否可以支援分區上傳大檔案、斷點續傳?
此方案是使用HTML表單上傳的方式上傳檔案,不支援基於分區上傳大檔案和基於分區斷點續傳的情境。如果您想實現分區上傳大檔案或斷點續傳,請參考服務端產生STS臨時訪問憑證。
如何防止上傳的檔案被覆蓋
如果希望防止檔案覆蓋,可以在上傳請求的Header中添加x-oss-forbid-overwrite
參數,並將其值設為true
。例如:
formData.append('x-oss-forbid-overwrite', 'true');