zoukankan      html  css  js  c++  java
  • 阿里云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("{
    " +
                    "    "Version": "1",
    " +
                    "    "Statement": [
    " +
                    "     {
    " +
                    "           "Effect": "Allow",
    " +
                    "           "Action": [
    " +
                    "             "oss:PutObject"
    " +
                    "           ],
    " +
                    "           "Resource": [
    " +
                    "             "acs:oss:*:*:bucket-name/dir/*"" +
                    "           ]
    " +
                    "     }
    " +
                    "    ]
    " +
                    "}");
            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();
            }
        }
    }
    

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

  • 相关阅读:
    html5 桌面提醒:Notifycations
    windows 下 apache 的虚拟主机配置
    javascript 跨域
    javascript 类型数组读取二进制数据
    javascript parseInt() 函数的进制转换陷阱
    javascript 中几个与正则表达式相关的应用
    javascript 中的二进制运算
    一段小代码,发布网页时为js 、css 文件加上版本号
    base64 编码及解码
    PHP 的比较运算与逻辑运算
  • 原文地址:https://www.cnblogs.com/vchar/p/14887971.html
Copyright © 2011-2022 走看看