web端文件直傳阿里OSS


1、STS臨時授權訪問OSS

OSS 可以通過阿里雲STS(Security Token Service)進行臨時授權訪問。通過STS,可以為第三方應用或子用戶(即用戶身份由自己管理的用戶)頒發一個自定義時效和權限的訪問憑證。

1、使用場景

對於您本地身份系統所管理的用戶,例如您的App的用戶、您的企業本地賬號、第三方App的用戶,將這部分用戶稱為聯盟用戶。此外,聯盟用戶還可以是您創建的能訪問您的阿里雲資源應用程序的用戶。這些聯盟用戶可能需要直接訪問OSS資源。

對於這部分聯盟用戶,通過阿里雲STS服務為阿里雲賬號(或RAM用戶)提供臨時訪問權限管理。您不需要透露雲賬號(或RAM用戶)的長期密鑰(如登錄密碼、AccessKey),只需要生成一個臨時訪問憑證給聯盟用戶使用即可。這個憑證的訪問權限及有效期限都可以由您自定義。您不需要關心權限撤銷問題,臨時訪問憑證過期后會自動失效。

通過STS生成的臨時訪問憑證包括安全令牌 (SecurityToken)、臨時訪問密鑰STS AK(AccessKeyId和AccessKeySecret)。使用AccessKey方法與您在使用阿里雲賬戶或RAM用戶AccessKey發送請求時的方法相同。需要注意的是在每個向OSS發送的請求中必須攜帶安全令牌。

2、實現原理

以一個移動App舉例。假設你是一個移動App開發者,打算使用阿里雲OSS服務來保存App的終端用戶數據,並且保證每個App用戶之間的數據隔離,放置一個App用戶獲取到其他App用戶的數據。你可以使用STS授權用戶直接訪問OSS。

使用STS授權用戶直接訪問OSS的流程如下:

img

  1. App用戶登錄。App用戶和雲賬號無關,它是App的終端用戶,App服務器支持App用戶登錄。對於每個有效的App用戶來說,需要App服務器能定義出每個App用戶的最小訪問權限。

  2. App服務器請求STS服務獲取一個安全令牌(SecurityToken)。在調用STS之前,App服務器需要確定App用戶的最小訪問權限(用RAM Policy來自定義授權策略)以及憑證的過期時間。然后通過扮演角色(AssumeRole)來獲取一個代表角色身份的安全令牌(SecurityToken)。

  3. STS返回給App服務器一個臨時訪問憑證,包括一個安全令牌(SecurityToken)、臨時訪問密鑰(AccessKeyId和AccessKeySecret)以及過期時間。

  4. App服務器將臨時訪問憑證返回給App客戶端,App客戶端可以緩存這個憑證。當憑證失效時,App客戶端需要向App服務器申請新的臨時訪問憑證。例如,臨時訪問憑證有效期為1小時,那么App客戶端可以每30分鍾向App服務器請求更新臨時訪問憑證。

  5. App客戶端使用本地緩存的臨時訪問憑證去請求OSS API。OSS收到訪問請求后,會通過STS服務來驗證訪問憑證,正確響應用戶請求。

3、操作步驟

1、創建子賬號

img

復制保存一下創建用戶的accessKeyId和accessKeySecret,后面代碼中會用到,然后點擊添加權限,為其添加 AliyunSTSAssumeRoleAccess 權限。

img

img

2、創建權限策略

{
    "Version": "1",
    "Statement": [
     {
           "Effect": "Allow",
           "Action": [
             "oss:PutObject"
           ],
           "Resource": [
             "acs:oss:*:*:bucketName",
             "acs:oss:*:*:bucketName/*"
           ]
     }
    ]
}

img

3、創建角色並記錄角色ARN

img

為創建的角色添加第二步創建的自定義權限策略:

img

img

復制保存創建角色的ARN,后面代碼中用到:

img

4、調用STS服務接口AssumeRole獲取臨時訪問憑證

1、pom.xml文件添加依賴
<dependency>
    <groupId>com.aliyun</groupId>
    <artifactId>aliyun-java-sdk-sts</artifactId>
    <version>3.0.0</version>
</dependency>
<dependency>
    <groupId>com.aliyun</groupId>
    <artifactId>aliyun-java-sdk-core</artifactId>
    <version>4.4.6</version>
</dependency>
2、后端代碼實現
@Configuration
@ConfigurationProperties(prefix = "ali.oss")
@Data
public class AliOssConfig {
    private String endpoint;
    private String accessKeyId;
    private String accessKeySecret;
    private String roleArn;
    private String regionId;
    private String bucket;
}
ali:
  oss:
    endpoint: oss-cn-shenzhen.aliyuncs.com
    access-key-id: 用戶的accessKeyId
    access-key-secret: 用戶的accessKeySecret
    role-arn: 角色的ARN
    region-id: cn-shenzhen
    bucket: best-favorites

調用AssumeRole接口之后返回給前端的對象

@Data
@Builder
public class AliOssTokenVo {
    private String region;
    private String accessKeyId;
    private String accessKeySecret;
    private String stsToken;
    private String bucket;
}

獲取臨時訪問憑證的方法

 @Override
public AliOssTokenVo getOssToken() throws ClientException {
    IClientProfile profile = DefaultProfile.getProfile(aliOssConfig.getRegionId(), aliOssConfig.getAccessKeyId(), aliOssConfig.getAccessKeySecret());
    DefaultAcsClient client = new DefaultAcsClient(profile);
    final AssumeRoleRequest request = new AssumeRoleRequest();
    request.setRoleArn(aliOssConfig.getRoleArn());
    request.setRoleSessionName("best-favorites");
    request.setDurationSeconds(1000L);
    AssumeRoleResponse response = client.getAcsResponse(request);
    AssumeRoleResponse.Credentials credentials = response.getCredentials();
    String accessKeyId = credentials.getAccessKeyId();
    String accessKeySecret = credentials.getAccessKeySecret();
    String securityToken = credentials.getSecurityToken();
    return AliOssTokenVo.builder()
        .accessKeyId(accessKeyId)
        .accessKeySecret(accessKeySecret)
        .stsToken(securityToken)
        .region("oss-" + aliOssConfig.getRegionId())
        .bucket(aliOssConfig.getBucket())
        .build();
}
3、前端代碼實現

api,調用后端接口獲取臨時訪問憑證ossToken

import service from '@/utils/request';

export const getOssToken = () => service.get('/ali-oss/token');

store/modules/oss模塊

import { getOssToken } from '@/api/business';

const oss = {
  state: {
    accessKeyId: '',
    accessKeySecret: '',
    stsToken: '',
    region: '',
    bucket: '',
  },
  mutations: {
    SET_OSS_TOKEN: (state, { accessKeyId, accessKeySecret, stsToken, region, bucket }) => {
      state.accessKeyId = accessKeyId;
      state.accessKeySecret = accessKeySecret;
      state.stsToken = stsToken;
      state.region = region;
      state.bucket = bucket;
    },
  },
  actions: {
    GetOssToken({ commit }) {
      return new Promise((resolve, reject) => {
        getOssToken()
          .then(res => {
            const ossToken = res.result;
            commit('SET_OSS_TOKEN', ossToken);
            resolve(ossToken);
          })
          .catch(error => {
            reject(error);
          });
      });
    },
  },
};

export default oss;

上傳組件:

<div class="clearfix">
  <a-upload
            list-type="picture-card"
            :file-list="fileList"
            :before-upload="beforeUpload"
            :custom-request="customRequest"
            @preview="handlePreview"
            @change="handleChange"
            >
    <div v-if="fileList.length < 1">
      <a-icon type="plus" />
      <div class="ant-upload-text">
        Upload
      </div>
    </div>
  </a-upload>
  <a-modal :visible="previewVisible" :footer="null" @cancel="handleCancel">
    <img alt="example" style="width: 100%" :src="previewImage" />
  </a-modal>
</div>
beforeUpload(file) {
  const isJpgOrPng = file.type === 'image/jpeg' || file.type === 'image/png';
  if (!isJpgOrPng) {
    this.$message.error('只能上傳圖片!');
  }
  const isLt10M = file.size / 1024 / 1024 < 10;
  if (!isLt10M) {
    this.$message.error('上傳圖片必須小於10M!');
  }
  return isJpgOrPng && isLt10M;
},
  async handleUpload({ file }) {
    try {
      let ossToken = await this.$store.dispatch('GetOssToken');
      const client = new OSS({
        accessKeyId: ossToken.accessKeyId,
        accessKeySecret: ossToken.accessKeySecret,
        stsToken: ossToken.stsToken,
        region: ossToken.region,
        bucket: ossToken.bucket,
      });
      let result = await client.put(file.name, file);
      console.log('result:', result);
      file.url = result.url;
      this.fileList = [...this.fileList, file];
      this.$emit('uploadSuccess', file.url);
      this.$message.success('文件上傳成功!');
    } catch (e) {
      console.log(e);
      this.$message.error('文件上傳失敗!');
    }
  },

由於是使用web端直傳,點擊上傳之后可能會出現如下錯誤,說明阿里雲oss需要配置一下跨域訪問

img

img

img

點擊確定之后,重新上傳,可以看到這時就已經上傳成功!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM