1.背景

采用JavaScript客户端直接签名时,AccessKeyID和AcessKeySecret会暴露在前端页面,因此存在严重的安全隐患。因此,OSS提供了服务端签名后直传的方案。

2.原理介绍

在这里插入图片描述
服务端签名后直传的原理如下:

1.用户发送上传Policy请求到应用服务器。
2.应用服务器返回上传Policy和签名给用户。
3.用户直接上传数据到OSS。

本示例中,Web端向服务端请求签名,然后直接上传,不会对服务端产生压力,而且安全可靠。但本示例中的服务端无法实时了解用户上传了多少文件,上传了什么文件。如果想实时了解用户上传了什么文件,可以采用服务端签名直传并设置上传回调。详见下片文章阿里oss服务端签名直传并设置上传回调

官方文档地址:服务端签名后直传

3.OSS中的相关概念

Endpoint:访问域名,通过该域名可以访问OSS服务的API,进行文件上传、下载等操作。
Bucket:存储空间,是存储对象的容器,所有存储对象都必须隶属于某个存储空间。
Object:对象,对象是 OSS 存储数据的基本单元,也被称为 OSS 的文件。
AccessKey:访问密钥,指的是访问身份验证中用到的 AccessKeyId 和 AccessKeySecret。

之前有一篇文章已经介绍了各个参数的位置。如有不知道的,请看这篇springboot整合oss实现文件的上传,查看,删除,下载

4.编码

在pom.xml中添加相关依赖



	<!-- OSS SDK 相关依赖 -->
        <dependency>
            <groupId>com.aliyun.oss</groupId>
            <artifactId>aliyun-sdk-oss</artifactId>
            <version>2.5.0</version>
        </dependency>


修改SpringBoot配置文件

server:
  port: 80
# OSS相关配置信息
aliyun:
  oss:
   endpoint: endpoint # oss对外服务的访问域名
   accessKeyId: accessKeyId # 访问身份验证中用到用户标识
   accessKeySecret: accessKeySecret # 用户用于加密签名字符串和oss用来验证签名字符串的密钥
   bucketName: tuanzi-ne # oss的存储空间
   policy:
    expire: 300 # 签名有效期(S)
   maxSize: 10 # 上传文件大小(M)
   dir:
    prefix: tuanzi/images/   # 上传文件夹路径前缀

添加OSS的相关Java配置

用于配置OSS的连接客户端OSSClient。
package com.tuanzi.config;

import com.aliyun.oss.OSSClient;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * oss配置文件
 * @auther 团子
 * @date 2019-07-31 10:03
 */
@Configuration
@Data
public class OssConfig {

    @Value("${aliyun.oss.endpoint}")
    private String ALIYUN_OSS_ENDPOINT;

    @Value("${aliyun.oss.accessKeyId}")
    private String ALIYUN_OSS_ACCESSKEYID;

    @Value("${aliyun.oss.accessKeySecret}")
    private String ALIYUN_OSS_ACCESSKEYSECRET;
    
    @Bean
    public OSSClient ossClient(){

        return new OSSClient(ALIYUN_OSS_ENDPOINT, ALIYUN_OSS_ACCESSKEYID, ALIYUN_OSS_ACCESSKEYSECRET);

    }
}

添加OSS上传策略封装对象OssPolicyResult

前端直接上传文件时所需参数,从后端返回过来。
package com.tuanzi.dto;

import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

/**
 * 获取OSS上传文件授权返回结果
 * @auther 团子
 * @date 2019-07-31 11:31
 */

@Data
public class OssPolicyResult {
    @ApiModelProperty("访问身份验证中用到用户标识")
    private String accessKeyId;
    @ApiModelProperty("用户表单上传的策略,经过base64编码过的字符串")
    private String policy;
    @ApiModelProperty("对policy签名后的字符串")
    private String signature;
    @ApiModelProperty("上传文件夹路径前缀")
    private String dir;
    @ApiModelProperty("oss对外服务的访问域名")
    private String host;
}

添加OssController定义接口

/**
 * Oss相关操作接口
 * @auther 团子
 * @date 2019-07-31 11:31
 */

@Controller
@Api(tags = "OssController", description = "Oss管理")
@RequestMapping("/aliyun/oss")
public class OssController {

    public static final Logger logger = LoggerFactory.getLogger(OssController.class);

    @Autowired
    private OssServiceImpl ossService;

    @ApiOperation(value = "oss上传签名生成")
    @RequestMapping(value = "/policy", method = RequestMethod.GET)
    @ResponseBody
    public CommonResult<OssPolicyResult> policy() {
        logger.info("policy --------------> start");
        OssPolicyResult result = ossService.policy();
        logger.info("policy --------------> end");
        return CommonResult.success(result);
    }
 }

添加OSS业务接口OssService

/**
 * oss上传管理Service
 * @auther 团子
 * @date 2019-07-31 11:31
 */

public interface OssService {
    /**
     * oss上传策略生成
     */
    OssPolicyResult policy();
 }

添加OSS业务接口OssService的实现类OssServiceImpl

    
    private static final Logger LOGGER = LoggerFactory.getLogger(OssServiceImpl.class);
	@Value("${aliyun.oss.policy.expire}")
	private int ALIYUN_OSS_EXPIRE;
	@Value("${aliyun.oss.maxSize}")
	private int ALIYUN_OSS_MAX_SIZE;
	@Value("${aliyun.oss.bucketName}")
	private String ALIYUN_OSS_BUCKET_NAME;
	@Value("${aliyun.oss.endpoint}")
	private String ALIYUN_OSS_ENDPOINT;
	@Value("${aliyun.oss.dir.prefix}")
	private String ALIYUN_OSS_DIR_PREFIX;
    @Autowired
	private OSSClient ossClient;

   /**
	 * 签名生成
	 */
	@Override
	public OssPolicyResult policy() {
		OssPolicyResult result = new OssPolicyResult();
		// 存储目录
		SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
		String dir = ALIYUN_OSS_DIR_PREFIX+sdf.format(new Date());
		// 签名有效期
		long expireEndTime = System.currentTimeMillis() + ALIYUN_OSS_EXPIRE * 1000;
		Date expiration = new Date(expireEndTime);
		// 文件大小
		long maxSize = ALIYUN_OSS_MAX_SIZE * 1024 * 1024;

		// 提交节点
		String action = "http://" + ALIYUN_OSS_BUCKET_NAME + "." + ALIYUN_OSS_ENDPOINT;
		try {
			PolicyConditions policyConds = new PolicyConditions();
			policyConds.addConditionItem(PolicyConditions.COND_CONTENT_LENGTH_RANGE, 0, maxSize);
			policyConds.addConditionItem(MatchMode.StartWith, PolicyConditions.COND_KEY, dir);
			String postPolicy = ossClient.generatePostPolicy(expiration, policyConds);
			byte[] binaryData = postPolicy.getBytes("utf-8");
			String policy = BinaryUtil.toBase64String(binaryData);
			String signature = ossClient.calculatePostSignature(postPolicy);
		result.setAccessKeyId(ossClient.getCredentialsProvider().getCredentials().getAccessKeyId());
			result.setPolicy(policy);
			result.setSignature(signature);
			result.setDir(dir);
			result.setHost(action);
		} catch (Exception e) {
			LOGGER.error("签名生成失败", e);
		}
		return result;
	}

这样我们就全写完了。我们来进行接口测试!
这里我利用postman进行演示

会调用两次请求,第一次访问本地接口获取上传的策略
在这里插入图片描述
第二次调用oss服务 的接口进行文件上传
在这里插入图片描述
我们再来看看oss管理控制台
在这里插入图片描述

源码地址

代码地址

Q.E.D.