1. 程式人生 > 其它 >阿里雲OSS檔案上傳前端搭配之後端的活

阿里雲OSS檔案上傳前端搭配之後端的活

原文:https://blog.vchar.top/java/1622894400.html

雖然不用後端額外的處理,前端也可以根據阿里雲的提供的方法直接操作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();
        }
    }
}

這裡返回給前端同學的資訊中最好將過期時間也返回,這樣可以判斷是否過期以便可以主動獲取新的資訊(上傳大檔案時比較耗時)。