oss服务端签名后直传分析与代码实现

Dear 丶 2024-04-24 14:35 163阅读 0赞

文章目录

  • 1.简介
    • 1.1 普通上传方式
    • 1.2 服务端签名后直传
  • 3.服务端签名后直传文档
    • 3.1 用户向应用服务器请求上传Policy和回调。
    • 3.2 应用服务器返回上传Policy和签名给用户。
    • 3.3 用户使用Post方法向OSS发送文件上传请求。
  • 4.实战开发-后端
    • 4.1 pom.xml核心配置
    • 4.2 application.yml核心配置
    • 4.3 OssClientUtils工具类
    • 4.4 OssConfig配置类
    • 4.5 OssConstants常量类
    • 4.6 OssServiceController控制器
    • 4.7 Apifox测试
  • 5.注意事项和细节说明
    • 5.1 解决后端跨域问题
    • 5.2 解决阿里云跨域问题

1.简介

阿里云对象存储 OSS(Object Storage Service)是一款海量、安全、低成本、高可靠的云存储服务,提供数据高可用性, 多种存储类型供选择,全面优化存储成本。

1.1 普通上传方式

image-20221113114912837

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

1.2 服务端签名后直传

image-20221113115032842

  • Web端向服务端请求签名,然后直接上传,不会对服务端产生压力,而且安全可靠。
  • 但服务端无法实时了解用户上传了多少文件,上传了什么文件。如果想实时了解用户上传了什么文件,可以采用服务端签名直传并设置上传回调。
  • 但存在着恶意上传的风险,造成存储空间的浪费

3.服务端签名后直传文档

? https://help.aliyun.com/document\_detail/31926.html

基于Post Policy的使用规则在服务端通过各种语言代码完成签名,然后通过表单直传数据到OSS。由于服务端签名直传无需将AccessKey暴露在前端页面,相比JavaScript客户端签名直传具有更高的安全性。

image-20221113145618805

3.1 用户向应用服务器请求上传Policy和回调。

请将客户端源码中的upload.js文件的如下代码片段的变量serverUrl的值设置为应用服务器的URL。

  1. // serverUrl是用户获取签名和Policy等信息的应用服务器的URL,请将下面的IP和Port配置为您自己的真实信息。
  2. serverUrl = 'http://88.88.88.88:8888'

设置完成后,客户端会向该serverUrl发送Get请求来获取需要的信息。客户端源码下载地址,请参见aliyun-oss-appserver-js-master.zip。

本场景为服务端签名后直传,不涉及上传回调。因此,您需要注释客户端源码的upload.js文件内的'callback' : callbackbody字段,以关闭上传回调功能。

  1. {
  2. 'key' : key + '${filename}',
  3. 'policy': policyBase64,
  4. 'OSSAccessKeyId': accessid,
  5. // 设置服务端返回200状态码,默认返回204。
  6. 'success_action_status' : '200',
  7. 'callback' : callbackbody,
  8. 'signature': signature,
  9. }

3.2 应用服务器返回上传Policy和签名给用户。

应用服务器侧的签名直传服务会处理客户端发送的Get请求消息,您可以设置对应的代码让应用服务器能够给客户端返回正确的消息。

以下是签名直传服务返回给客户端消息Body内容的示例:

  1. {
  2. "accessid":"LTAI5tBDFVar1hoq****",
  3. "host":"http://post-test.oss-cn-hangzhou.aliyuncs.com",
  4. "policy":"eyJleHBpcmF0aW9uIjoiMjAxNS0xMS0wNVQyMDoyMzoyM1oiLCJjxb25kaXRpb25zIjpbWyJjcb250ZW50LWxlbmd0aC1yYW5nZSIsMCwxMDQ4NTc2MDAwXSxbInN0YXJ0cy13aXRoIiwiJGtleSIsInVzZXItZGlyXC8i****",
  5. "signature":"VsxOcOudx******z93CLaXPz+4s=",
  6. "expire":1446727949,
  7. "dir":"user-dirs/"
  8. }

Body中的各字段说明如下:


































字段 描述
accessid 用户请求的AccessKey ID。
host 用户发送上传请求的域名。
policy 用户表单上传的策略(Policy),Policy为经过Base64编码过的字符串。详情请参见Post Policy
signature 对Policy签名后的字符串。详情请参见Post Signature
expire 由服务器端指定的Policy过期时间,格式为Unix时间戳(自UTC时间1970年01月01号开始的秒数)。
dir 限制上传的文件前缀。

3.3 用户使用Post方法向OSS发送文件上传请求。

  1. new_multipart_params = {
  2. // key表示上传到Bucket内的Object的完整路径,例如exampledir/exampleobject.txtObject,完整路径中不能包含Bucket名称。
  3. // filename表示待上传的本地文件名称。
  4. 'key' : key + '${filename}',
  5. 'policy': policyBase64,
  6. 'OSSAccessKeyId': accessid,
  7. // 设置服务端返回状态码为200,不设置则默认返回状态码204。
  8. 'success_action_status' : '200',
  9. 'signature': signature,
  10. };

4.实战开发-后端

4.1 pom.xml核心配置

  1. <dependencyManagement>
  2. <dependencies>
  3. <dependency>
  4. <groupId>com.alibaba.cloud</groupId>
  5. <artifactId>spring-cloud-alibaba-dependencies</artifactId>
  6. <version>2.1.0.RELEASE</version>
  7. <type>pom</type>
  8. <scope>import</scope>
  9. </dependency>
  10. </dependencies>
  11. </dependencyManagement>
  12. <dependencies>
  13. <dependency>
  14. <groupId>com.alibaba.cloud</groupId>
  15. <artifactId>spring-cloud-starter-alicloud-oss</artifactId>
  16. <!--<version>2.1.0.RELEASE</version>-->
  17. </dependency>

4.2 application.yml核心配置

  1. server:
  2. port: 9101
  3. max-http-header-size: 2MB
  4. servlet:
  5. context-path: /service
  6. spring:
  7. cloud:
  8. alicloud:
  9. access-key: LTAI5tKcWoeuxTK8********
  10. secret-key: TQXiDtEnaR8Y7tXetvYZ0t********
  11. oss:
  12. endpoint: oss-cn-hangzhou.aliyuncs.com
  13. bucket: waveedu

4.3 OssClientUtils工具类

  1. package com.zhulang.waveedu.sms.util;
  2. import com.aliyun.oss.OSS;
  3. import com.aliyun.oss.common.utils.BinaryUtil;
  4. import com.aliyun.oss.model.MatchMode;
  5. import com.aliyun.oss.model.PolicyConditions;
  6. import com.zhulang.waveedu.common.entity.Result;
  7. import java.text.SimpleDateFormat;
  8. import java.util.Date;
  9. import java.util.LinkedHashMap;
  10. import java.util.Map;
  11. /**
  12. * @author 狐狸半面添
  13. * @create 2023-01-29 1:36
  14. */
  15. public class OssClientUtils {
  16. private OSS ossClient;
  17. private String accessId;
  18. private String endpoint;
  19. private String bucket;
  20. public OssClientUtils(OSS ossClient, String accessId, String endpoint, String bucket) {
  21. this.ossClient = ossClient;
  22. this.accessId = accessId;
  23. this.endpoint = endpoint;
  24. this.bucket = bucket;
  25. }
  26. public Result policy(String dirType) {
  27. // 1.指定填写Host地址,格式为https://bucketname.endpoint
  28. String host = "https://" + bucket + "." + endpoint;
  29. // 2.设置上传到OSS文件的前缀,可置空此项。置空后,文件将上传至Bucket的根目录下。
  30. String format = new SimpleDateFormat("/yyyy/MM/").format(new Date());
  31. String dir = dirType + format;
  32. Map<String, String> respMap = null;
  33. try {
  34. // 3.指定默认超时时间是30s
  35. long expireTime = 30;
  36. // 4.得到最终的截止时间
  37. long expireEndTime = System.currentTimeMillis() + expireTime * 1000;
  38. Date expiration = new Date(expireEndTime);
  39. // 5.封装签名
  40. PolicyConditions policyConds = new PolicyConditions();
  41. // 5.1 设置可上传文件的大小,这里设置为 0 - 10MB
  42. policyConds.addConditionItem(PolicyConditions.COND_CONTENT_LENGTH_RANGE, 0, 10485760);
  43. // 5.2 设置上传文件的前缀、可忽略
  44. policyConds.addConditionItem(MatchMode.StartWith, PolicyConditions.COND_KEY, dir);
  45. // 5.3 对Policy签名后的字符串。
  46. String postPolicy = ossClient.generatePostPolicy(expiration, policyConds);
  47. String postSignature = ossClient.calculatePostSignature(postPolicy);
  48. // 6.用户表单上传的策略(Policy),Policy为经过Base64编码过的字符串。
  49. byte[] binaryData = postPolicy.getBytes("utf-8");
  50. String encodedPolicy = BinaryUtil.toBase64String(binaryData);
  51. // 7.封装签名直传服务返回给客户端消息Body内容
  52. respMap = new LinkedHashMap<>();
  53. // 7.1 用户请求的AccessKey ID。
  54. respMap.put("accessid", accessId);
  55. // 7.2 用户表单上传的策略(Policy),Policy为经过Base64编码过的字符串。
  56. respMap.put("policy", encodedPolicy);
  57. // 7.3 对Policy签名后的字符串。
  58. respMap.put("signature", postSignature);
  59. // 7.4 限制上传的文件前缀。
  60. respMap.put("dir", dir);
  61. // 7.5 用户发送上传请求的域名。
  62. respMap.put("host", host);
  63. // 7.6 由服务器端指定的Policy过期时间,格式为Unix时间戳(自UTC时间1970年01月01号开始的秒数)。
  64. respMap.put("expire", String.valueOf(expireEndTime / 1000));
  65. } catch (Exception e) {
  66. return Result.error();
  67. }
  68. return Result.ok(respMap);
  69. }
  70. }

4.4 OssConfig配置类

  1. package com.zhulang.waveedu.sms.config;
  2. import com.aliyun.oss.OSS;
  3. import com.zhulang.waveedu.sms.util.OssClientUtils;
  4. import org.springframework.beans.factory.annotation.Value;
  5. import org.springframework.context.annotation.Bean;
  6. import org.springframework.context.annotation.Configuration;
  7. import javax.annotation.Resource;
  8. /**
  9. * @author 狐狸半面添
  10. * @create 2023-01-29 1:41
  11. */
  12. @Configuration
  13. public class OssConfig {
  14. @Resource
  15. private OSS ossClient;
  16. @Value("${spring.cloud.alicloud.access-key}")
  17. private String accessId;
  18. @Value("${spring.cloud.alicloud.oss.endpoint}")
  19. private String endpoint;
  20. @Value("${spring.cloud.alicloud.oss.bucket}")
  21. private String bucket;
  22. @Bean
  23. public OssClientUtils ossClientUtils() {
  24. return new OssClientUtils(ossClient, accessId, endpoint, bucket);
  25. }
  26. }

4.5 OssConstants常量类

  1. package com.zhulang.waveedu.sms.constant;
  2. /**
  3. * 与Oss相关的常量
  4. *
  5. * @author 狐狸半面添
  6. * @create 2023-01-29 1:55
  7. */
  8. public class OssConstants {
  9. public static final String HEAD_IMAGE_DIR = "head-image";
  10. }

4.6 OssServiceController控制器

  1. package com.zhulang.waveedu.sms.controller;
  2. import com.zhulang.waveedu.common.entity.Result;
  3. import com.zhulang.waveedu.sms.constant.OssConstants;
  4. import com.zhulang.waveedu.sms.util.OssClientUtils;
  5. import org.springframework.web.bind.annotation.RequestMapping;
  6. import org.springframework.web.bind.annotation.RestController;
  7. import javax.annotation.Resource;
  8. /**
  9. * @author 狐狸半面添
  10. * @create 2023-01-29 1:30
  11. */
  12. @RestController
  13. @RequestMapping("/oss")
  14. public class OssServiceController {
  15. @Resource
  16. private OssClientUtils ossClientUtils;
  17. /**
  18. * 获取头像的签名
  19. *
  20. * @return 签名信息
  21. */
  22. @RequestMapping("/headImage")
  23. public Result headImage(){
  24. return ossClientUtils.policy(OssConstants.HEAD_IMAGE_DIR);
  25. }
  26. }

4.7 Apifox测试

image-20230129160309981

5.注意事项和细节说明

5.1 解决后端跨域问题

  1. package com.zhulang.waveedu.basic.config;
  2. import org.springframework.context.annotation.Configuration;
  3. import org.springframework.web.servlet.config.annotation.CorsRegistry;
  4. import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
  5. /**
  6. * 解决跨域问题
  7. *
  8. * @author 狐狸半面添
  9. * @create 2023-01-15 23:08
  10. */
  11. @Configuration
  12. public class CorsConfig implements WebMvcConfigurer {
  13. @Override
  14. public void addCorsMappings(CorsRegistry registry) {
  15. registry.addMapping("/**")
  16. .allowedOrigins("*")
  17. .allowCredentials(true)
  18. .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
  19. .maxAge(3600);
  20. }
  21. }

5.2 解决阿里云跨域问题

image-20230129160804310

发表评论

表情:
评论列表 (有 0 条评论,163人围观)

还没有评论,来说两句吧...

相关阅读