阿里云OSS文件上传前端搭配之后端的活
虽然不用后端额外的处理,前端也可以根据阿里云的提供的方法直接操作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();
}
}
}
这里返回给前端同学的信息中最好将过期时间也返回,这样可以判断是否过期以便可以主动获取新的信息(上传大文件时比较耗时)。