阿里云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();
        }
    }
}

这里返回给前端同学的信息中最好将过期时间也返回,这样可以判断是否过期以便可以主动获取新的信息(上传大文件时比较耗时)。

posted @ 2021-06-16 08:58  起岸星辰  阅读(2325)  评论(0编辑  收藏  举报