雖然不用后端額外的處理,前端也可以根據阿里雲的提供的方法直接操作OSS對象存儲來上傳文件。但是由於前端的js文件是直接暴露給用戶的,即使現如今前端的什么js混淆加密等待處理方式,但是其最終都會被用戶給看到,只是查找的成本高了一點而已。因此直接使用accessKeyId和accessKeySecret是非常不安全的,因此阿里雲的對象存儲OSS根據上傳需求提供如下2種方式來進行文件的上傳。本文 參考文檔 。
服務端簽名后直傳
服務端簽名后直傳:這種方式適用於普通的單文件上傳,其流程是前端請求后端獲取一個token(有時效性),之后通過這個token來給阿里雲鑒權,然后上傳文件;這樣將不會直接將accessKeyId和accessKeySecret直接暴露給用戶了。其流程圖如下:
官方文檔地址: Web端PostObject直傳實踐
Java生成簽名的實現
public void generateToken(){
// 授權訪問oss的ack
String accessId = "<yourAccessKeyId>";
String accessKey = "<yourAccessKeySecret>";
// 所屬地域
String endpoint = "oss-cn-chengdu.aliyuncs.com";
// bucket的名稱
String bucket = "bucket-name";
// 格式為https://bucketname.endpoint,例如https://bucket-name.oss-cn-chengdu.aliyuncs.com
String host = "https://" + bucket + "." + endpoint;
// 要上傳的目錄,比如:train
String dir = "user-dir-prefix/";
// 設置token的過期時間,這里設置的1分鍾
long expireEndTime = System.currentTimeMillis() + 60*1000;
Date expiration = new Date(expireEndTime);
// 設置生成token的權限
PolicyConditions policyConditions = new PolicyConditions();
policyConditions.addConditionItem(PolicyConditions.COND_CONTENT_LENGTH_RANGE, 0, 1048576000);
policyConditions.addConditionItem(MatchMode.StartWith, PolicyConditions.COND_KEY, dir);
// 創建oss請求對象
OSS client = new OSSClientBuilder().build(endpoint, accessId, accessKey);
// 發送請求獲取token
String postPolicy = client.generatePostPolicy(expiration, policyConditions);
// 解析請求響應
byte[] binaryData = postPolicy.getBytes(StandardCharsets.UTF_8);
// 政策信息
String encodedPolicy = BinaryUtil.toBase64String(binaryData);
// 簽名信息
String postSignature = client.calculatePostSignature(postPolicy);
System.out.println("accessKeyId:"+accessId);
System.out.println("encodedPolicy:"+encodedPolicy);
System.out.println("postSignature:"+postSignature);
System.out.println("dir:"+dir);
System.out.println("host:"+host);
System.out.println("expire:"+(expireEndTime / 1000));
}
注意需要將要訪問bucket設置為運行跨域。
STS臨時授權訪問OSS
在進行小文件的上傳時,比如圖片這些;使用上面簽名的方式完全夠用了;但是當我們需要上傳大文件的時候,比如上傳幾百兆、幾個G的文件時就不太適用了,因為這種大文件上傳肯定是比較慢的,為了用戶體驗我們應當提高其上傳速度,同時顯示一個上傳進度條。此時就需要用到分片上傳和斷點續傳這種方式了。
先說明下什么是分片上傳?分片上傳就是將一個文件拆分為多個小文件,然后再分別將小文件上傳到服務端,由於文件很小因此速度自然也就很快,服務端收到上傳完畢后會將所有的小文件合並成一個文件,這樣我們的文件就上傳完成了。
由於此種操作模式可能復雜,因此簽名的方式阿里雲並沒有提供分片上傳支持,而是提供了一個叫做STS臨時授權的功能。
使用STS臨時授權訪問OSS時請注意授予的權限,因此此種方式可以干更多的事情,因此建議縮小其可以訪問的文件夾權限。
STS臨時授權訪問OSS的流程圖:
STS臨時授權訪問OSS流程
首先我們需要在阿里雲做如下配置:
步驟一:創建RAM用戶
- 登錄RAM控制台。
- 在左側導航欄的人員管理菜單下,單擊用戶。
- 單擊新建用戶。
- 輸入登錄名稱和顯示名稱。
- 在訪問方式區域下,選擇編程訪問,然后單擊確定。
- 單擊復制,保存訪問密鑰(AccessKey ID 和 AccessKey Secret)。
步驟二:為RAM用戶授予請求AssumeRole的權限
給我們剛剛創建的用戶授予AssumeRole的權限;之后我們就通過這個賬號來生成STS臨時授權的accessKeyId、accessKeySecret和token。
當然你也可以用你之前用來訪問OSS的那個賬號,但是為了保證安全,我們這里還是新建一個專門用來授權的賬號
- 單擊已創建RAM用戶右側對應的添加權限。
- 在添加權限頁面,選擇
AliyunSTSAssumeRoleAccess
權限;最后點擊確定按鈕。
步驟三:創建用於獲取臨時訪問憑證的角色
- 在左側導航欄,單擊RAM角色管理。
- 單擊創建RAM角色,選擇可信實體類型為阿里雲賬號,單擊下一步。
- 在創建RAM角色頁面,填寫RAM角色名稱,選擇雲賬號為當前雲賬號。
- 單擊完成。角色創建完成后,單擊關閉。
- 之后在RAM角色管理頁面,找到剛剛創建的RAM角色,將其ARN值拷貝下來。(我們待會生成授權信息的時候需要)
步驟四:為角色授予上傳文件的權限
- 在左側導航欄的權限管理菜單下,單擊權限策略管理。
- 單擊創建權限策略。
- 在新建自定義權限策略頁面,填寫策略名稱,配置模式選擇腳本配置,在里面可以進行相關的配置。下面這個是示例:
{
"Version": "1",
"Statement": [
{
"Effect": "Allow",
"Action": [
"oss:PutObject"
],
"Resource": [
"acs:oss:*:*:bucket-test/dir/*",
"acs:oss:*:*:bucket-demo/train/*"
]
}
]
}
配置文件標明授權其訪問 bucket名稱為bucket-test的名叫dir的目錄,以及bucket名稱為bucket-demo的名叫train的目錄。
由於我們使用STS授權時,通常是為了使用它的分片上傳,因此應當授予如下的操作(即完整的配置為):
{
"Version": "1",
"Statement": [
{
"Effect": "Allow",
"Action": [
"oss:PutObject",
"oss:InitiateMultipartUpload",
"oss:UploadPart",
"oss:UploadPartCopy",
"oss:CompleteMultipartUpload",
"oss:AbortMultipartUpload",
"oss:ListMultipartUploads",
"oss:ListParts"
],
"Resource": [
"acs:oss:*:*:bucket-test/dir/*",
"acs:oss:*:*:bucket-demo/train/*"
]
}
]
}
最后我們需要將創建的這個策略綁定到RAM角色上面去才能生效(通過添加權限按鈕,然后在自定義策略里面找)。
使用Java的sdk生成授權信息
Java生成STS臨時授權的accessKeyId、accessKeySecret和token實現
import com.alibaba.fastjson.JSONObject;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.auth.sts.AssumeRoleRequest;
import com.aliyuncs.auth.sts.AssumeRoleResponse;
import com.aliyuncs.profile.DefaultProfile;
import org.junit.Test;
public class OssStsTokenTest {
@Test
public void sstToken() {
// 區域
String regionId = "cn-chengdu";
// OSS的ack
String accessKeyId = "<yourAccessKeyId>";
String accessKeySecret = "<yourAccessKeySecret>";
// 創建OSSClient實例。
DefaultProfile profile = DefaultProfile.getProfile(regionId, accessKeyId, accessKeySecret);
IAcsClient client = new DefaultAcsClient(profile);
// 填寫步驟3獲取的角色ARN。
String roleArn = "<yourARN>";
// 標識臨時訪問憑證的名稱;這個可以按你需要隨便寫
String roleSessionName = "<yourRoleSessionName>";
AssumeRoleRequest request = new AssumeRoleRequest();
request.setRegionId(regionId);
request.setRoleArn(roleArn);
// 設置授權信息,注意如果這里沒有設置,那么默認會擁有上面設置的角色的權限
request.setPolicy("{\n" +
" \"Version\": \"1\",\n" +
" \"Statement\": [\n" +
" {\n" +
" \"Effect\": \"Allow\",\n" +
" \"Action\": [\n" +
" \"oss:PutObject\"\n" +
" ],\n" +
" \"Resource\": [\n" +
" \"acs:oss:*:*:bucket-name/dir/*\"" +
" ]\n" +
" }\n" +
" ]\n" +
"}");
request.setRoleSessionName(roleSessionName);
// 設置臨時訪問憑證的有效時間為3600秒。
request.setDurationSeconds(3600L);
try {
AssumeRoleResponse response = client.getAcsResponse(request);
System.out.println(JSONObject.toJSONString(response));
AssumeRoleResponse.Credentials credentials = response.getCredentials();
System.out.println(credentials.getAccessKeyId());
System.out.println(credentials.getAccessKeySecret());
System.out.println(credentials.getSecurityToken());
System.out.println(credentials.getExpiration());
} catch (Exception e) {
e.printStackTrace();
}
}
}
這里返回給前端同學的信息中最好將過期時間也返回,這樣可以判斷是否過期以便可以主動獲取新的信息(上傳大文件時比較耗時)。