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管理控制台
5.源码地址
完整源码地址:阿里oss服务端签名后直传