使用STS临时访问凭证通过客户端直连OSS对象存储服务器

目录

1、导论

2、客户端直传

3、创建RAM用户以及RAM角色

4、如何实现客户端直传

4.1、跨域访问

4.2、安全授权

5、代码示例

5.1、后端代码实例

5.2、客户端代码实例 


1、导论

        最近在做项目的过程中使用到了阿里云OSS来存储客户端上传的文件,方法是直接将客户端上传的文件发送到业务服务器,再通过业务服务器将文件上传到OSS对象存储服务器当中。

        起初觉得是什么问题的,但是在使用的过程中会发现上传文件的速度很慢,显然是受到业务服务器宽带的限制。为了解决这个问题我翻看了OSS的官方文档,在里面学到了不经过业务服务器的转发,而是直接让客户端和OSS服务器进行连接直接上传。下面为大家讲解一下在客户端直连OSS的操作过程。

2、客户端直传

        客户端直传是指客户端直接上传文件到对象存储OSS。相对于服务端代理上传,客户端直传避免了业务服务器中转文件,提高了上传速度,节省了服务器资源。

        在典型的服务端和客户端架构下,常见的文件上传方式是服务端代理上传:客户端将文件上传到业务服务器,然后业务服务器将文件上传到OSS。在这个过程中,一份数据需要在网络上传输两次,会造成网络资源的浪费,增加服务端的资源开销。为了解决这一问题,可以在客户端直连OSS来完成文件上传,无需经过业务服务器中转。

总结:

服务器代理上传和客户端直传相比,有三个缺点:

  1. 上传慢:用户数据需先上传到应用服务器,之后再上传到OSS。网络传输时间比直传到OSS多一倍。如果用户数据不通过应用服务器中转,而是直传到OSS,速度将大大提升。而且OSS采用BGP带宽,能保证各地各运营商之间的传输速度。
  2. 扩展性差:如果后续用户多了,应用服务器会成为瓶颈。
  3. 费用高:需要准备多台应用服务器。由于OSS上传流量是免费的,如果数据直传到OSS,不通过应用服务器,那么将能省下几台应用服务器。

3、创建RAM用户以及RAM角色

        在RAM访问控制工作台中,在左侧导航栏中选择“用户”,创建用户并勾选OpenAPI调用访问

        创建完毕后为该用户添加授权,搜索oss以及sts将管理oss的权限以及sts权限授权给创建的用户,此时使用该用户的AccessKeyId以及AccessKeySecret凭证就可以向STS获取临时凭证。

再通过左侧导航栏的角色,创建一个普通用户,并授权oss权限。

最后查看角色详情,可以看到角色的ARN,即 roleArn。

4、如何实现客户端直传

        实现客户端直传需要解决以下两个大问题:

4.1、跨域访问

        如果客户端是Web端或小程序,需要解决跨域访问被限制的问题。浏览器以及小程序容器出于安全考虑,通常都会限制跨域访问,这一限制也会限制客户端代码直连OSS。

4.2、安全授权

        而实现客户端直传的另外一个大问题就是安全授权问题。

        客户端要想实现上传,必须要得到AccessKeyId以及AccessKeySecret凭证,通过凭证才能向OSS发起请求。而如果直接将这两个身份凭证放到前端,用户很容易就能够通过F12获取到,一旦被获取,用户可以用来做任何事情,例如向OSS中存储大量垃圾数据。

         为了解决这一问题,OSS提供了STS临时访问凭证。在服务端使用STS SDK获取STS临时访问凭证,然后在客户端使用STS临时凭证和OSS SDK直接上传文件。客户端能重复使用服务端生成的STS临时访问凭证生成签名。

  1. 客户端向业务服务器请求临时访问凭证。

  2. 业务服务器使用STS SDK调用AssumeRole接口,获取临时访问凭证。

  3. STS生成并返回临时访问凭证给业务服务器。

  4. 业务服务器返回临时访问凭证给客户端。

  5. 客户端使用OSS SDK通过该临时访问凭证上传文件到OSS。

  6. OSS返回成功响应给客户端。

5、代码示例

5.1、后端代码实例

import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.http.MethodType;
import com.aliyuncs.profile.DefaultProfile;
import com.aliyuncs.profile.IClientProfile;
import com.aliyuncs.auth.sts.AssumeRoleRequest;
import com.aliyuncs.auth.sts.AssumeRoleResponse;
public class StsServiceSample {
    public static void main(String[] args) { 
        // STS服务接入点,例如sts.cn-hangzhou.aliyuncs.com。 
        String endpoint = "sts.cn-hangzhou.aliyuncs.com";
        String accessKeyId = "OSS_ACCESS_KEY_ID";
        String accessKeySecret = "OSS_ACCESS_KEY_SECRET";
        String roleArn = "OSS_STS_ROLE_ARN";
        // 自定义角色会话名称,用来区分不同的令牌,例如可填写为SessionTest。        
        String roleSessionName = "yourRoleSessionName";
        // 如果policy为空,则临时访问凭证将获得角色拥有的所有权限。      
        String policy = "{\n" +
                "    \"Version\": \"1\", \n" +
                "    \"Statement\": [\n" +
                "        {\n" +
                "            \"Action\": [\n" +
                "                \"oss:PutObject\"\n" +
                "            ], \n" +
                "            \"Resource\": [\n" +
                "                \"acs:oss:*:*:*\" \n" +
                "            ], \n" +
                "            \"Effect\": \"Allow\"\n" +
                "        }\n" +
                "    ]\n" +
                "}";
        // 临时访问凭证的有效时间,单位为秒。最小值为900,最大值以当前角色设定的最大会话时间为准。当前角色最大会话时间取值范围为3600秒~43200秒,默认值为3600秒。
        // 在上传大文件或者其他较耗时的使用场景中,建议合理设置临时访问凭证的有效时间,确保在完成目标任务前无需反复调用STS服务以获取临时访问凭证。
        Long durationSeconds = 3600L;
        try {
            // regionId表示RAM的地域ID。以华东1(杭州)地域为例,regionID填写为cn-hangzhou。也可以保留默认值,默认值为空字符串("")。
            String regionId = "";
            // 添加endpoint。适用于Java SDK 3.12.0及以上版本。
            DefaultProfile.addEndpoint(regionId, "Sts", endpoint);
            // 添加endpoint。适用于Java SDK 3.12.0以下版本。
            // DefaultProfile.addEndpoint("",regionId, "Sts", endpoint);
            // 构造default profile。
            IClientProfile profile = DefaultProfile.getProfile(regionId, accessKeyId, accessKeySecret);
            // 构造client。
            DefaultAcsClient client = new DefaultAcsClient(profile);
            final AssumeRoleRequest request = new AssumeRoleRequest();
            // 适用于Java SDK 3.12.0及以上版本。
            request.setSysMethod(MethodType.POST);
            // 适用于Java SDK 3.12.0以下版本。
            //request.setMethod(MethodType.POST);
            request.setRoleArn(roleArn);
            request.setRoleSessionName(roleSessionName);
            request.setPolicy(policy); 
            request.setDurationSeconds(durationSeconds); 
            final AssumeRoleResponse response = client.getAcsResponse(request);
            System.out.println("Expiration: " + response.getCredentials().getExpiration());
            System.out.println("Access Key Id: " + response.getCredentials().getAccessKeyId());
            System.out.println("Access Key Secret: " + response.getCredentials().getAccessKeySecret());
            System.out.println("Security Token: " + response.getCredentials().getSecurityToken());
            System.out.println("RequestId: " + response.getRequestId());
        } catch (ClientException e) {
            System.out.println("Failed:");
            System.out.println("Error code: " + e.getErrCode());
            System.out.println("Error message: " + e.getErrMsg());
            System.out.println("RequestId: " + e.getRequestId());
        }
    }
}

需要注意的是:

1、durationSeconds的最大取值是角色最大会话时间,如需调整最大实现需要前往RAM控制台进行修改。

 

2、由于频繁地调用STS服务会引起限流,因此需要对STS临时凭证做缓存处理,并在有效期前刷新。例如可以使用Redis进行缓存操作。

        将应用服务器获取到的STS临时访问凭证credentials传递给前端,前端通过解析credentials即可获得相应的凭证,从而通过凭证创建OSSClient并上传文件。

5.2、客户端代码实例 

// 从cookie中获取sts
let stsParameter = Base64.decode(getCookie("sts"));
// 如果cookie中没有,则向后端发起请求获取sts
if (stsParameter.length === 0) {
  await that.$axios.get("/user/sts").then(res => {
    stsParameter = res.data.data
    setCookie("sts", Base64.encode(JSON.stringify(stsParameter)), 60 * 60 * 2)
  })
} else {
  stsParameter = JSON.parse(stsParameter)
}
let cli = client(stsParameter);




export function client(data) {
  return new OSS({
    region: data.region,
    accessKeyId: data.credentials.accessKeyId,
    accessKeySecret:  data.credentials.accessKeySecret,
    bucket: data.bucketName,
    stsToken: data.credentials.securityToken,
    refreshSTSToken: async () => {
      const refreshToken = await axios.get("http://localhost:8890/api/user/sts");
      return {
        accessKeyId: refreshToken.credentials.accessKeyId,
        accessKeySecret: refreshToken.credentials.AccessKeySecret,
        stsToken: refreshToken.credentials.securityToken,
      };
    },
  })
}


前端通过解析credentials即可获得相应的凭证,从而通过凭证创建OSSClient并获得上传文件的权限。

【博主推荐】 

【Java多线程】面试常考 —— JUC(java.util.concurrent) 的常见类_java concurrent哪些类-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/zzzzzhxxx/article/details/136777947?spm=1001.2014.3001.5502【网络原理】TCP 协议中比较重要的一些特性(三)-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/zzzzzhxxx/article/details/136597348?spm=1001.2014.3001.5502【Java多线程】关于多线程的一些案例 —— 单例模式中的饿汉模式和懒汉模式以及阻塞队列_单例模式懒汉和饿汉 java s1==s2-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/zzzzzhxxx/article/details/136688859?spm=1001.2014.3001.5502

如果觉得作者写的不错,求给博主一个点赞支持一下,你们的支持是我更新的最大动力!

如果觉得作者写的不错,求给博主一个点赞支持一下,你们的支持是我更新的最大动力!

如果觉得作者写的不错,求给博主一个点赞支持一下,你们的支持是我更新的最大动力!

  • 19
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Hacynn

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值