阿里云STS临时令牌操作OSS云存储
参考:官方文档1官方文档2
STS获取临时令牌操作OSS云存储
项目集成了swagger2自动接口文档,如未集成,需要将@Api、@ApiOperation等注解去掉
为什么要使用sts去操作?
直接使用账号的权限去操作OSS,账号拥有所有权限,使用RAM创建账号子用户,分离部分权限出来,提高OSS安全性能。
前期准备
阿里云控制台操作步骤
通过OSS SDK与STS SDK的结合使用,实现使用STS临时授权访问OSS。假设有一个名为qizy-test的Bucket用于存储用户数据,现将利用用户子账号结合STS实现OSS权限控制访问。
- 创建用户子账号。
- 云账号登录RAM控制台。
- 在左侧导航栏的人员管理菜单下,单击用户。
- 单击新建用户。
- 输入登录名称和显示名称。
- 在访问方式区域下,选择编程访问。
- 单击确定,然后单击复制保存访问密钥(AccessKeyID 和 AccessKeySecret)。
- 勾选目标RAM用户,单击添加权限,被授权主体会自动填入。
- 在添加权限页面,为已创建子账号添加AliyunSTSAssumeRoleAccess权限。
- 为已创建子账号添加AliyunOSSFullAccess权限。
- 创建权限策略。
- 云账号登录RAM控制台。
- 在左侧导航栏的权限管理菜单下,单击权限策略管理。
- 单击新建权限策略。
- 填写策略名称和备注。
- 配置模式选择可视化配置或脚本配置。
以脚本配置为例,对ram-test添加ListObjects与GetObject等只读权限,在策略内容中配置脚本示例如下:
#我的Bucket名:“qizy-test”
{
"Version": "1",
"Statement": [
{
"Effect": "Allow",
"Action": [
"oss:ListBuckets",
"oss:GetBucketStat",
"oss:GetBucketInfo",
"oss:GetBucketAcl"
],
"Resource": "acs:oss:*:*:*"
},
{
"Effect": "Allow",
"Action": [
"oss:Get*",
"oss:Put*",
"oss:List*",
"oss:DeleteObject"
],
"Resource": "acs:oss:*:*:qizy-test"
},
{
"Effect": "Allow",
"Action": [
"oss:Get*",
"oss:Put*",
"oss:List*",
"oss:DeleteObject"
],
"Resource": "acs:oss:*:*:qizy-test/*"
}
]
}
# 脚本讲解:
# 第一段的意思是允许用户登录,
# 第二段允许操作bucket,
# 第三段允许操作bucket内的资源。
# 这样我们的子用户控制Oss某个Bucket配置就完成了,开发时只需要用到子用户的AccessKey ID和Access Key Secret
# 如果需要对权限进行限制,可以对以上代码进行修改。
- 创建角色并记录角色ARN。
- 云账号登录RAM控制台。
- 在左侧导航栏,单击RAM角色管理。
- 单击新建RAM角色,选择可信实体类型为阿里云账号,单击下一步。
- 在新建RAM角色页面,填写RAM角色名称和备注,本示例RAM角色名称为RamOssTest。
- 选择云账号为当前云账号。
- 单击完成。
- 单击为角色授权,被授权主体会自动填入。
- 在添加权限页面,选择自定义权限策略,添加步骤2中创建的权限策略。
- 记录角色的ARN。
添加权限策略后,页面如下图所示:
- 获取临时访问凭证STS AK与SecurityToken。
具体java中进行实现
详见下面的步骤
代码中集成
依赖
# pom.xml
<!-- aliyun STS -->
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-sts</artifactId>
<version>3.0.0</version>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-core</artifactId>
<version>4.1.1</version>
</dependency>
配置文件
#sts.properties
#阿里云STS配置
sts.endpoint=sts.cn-hangzhou.aliyuncs.com
sts.accessKeyId=LTAI4FrLPBGyB2KYrp******
sts.accessKeySecret=WJPEEnybNOhsjksVHyq******
sts.roleArn=acs:ram::1373540******9:role/ra******test
# 取个名称 用以唯一性标识
sts.roleSessionName=test-sts-securityToken
代码实现-获取accessToken
工具类
# STSUtil.java
package com.learning.ssm_swagger.util;
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.sts.model.v20150401.AssumeRoleRequest;
import com.aliyuncs.sts.model.v20150401.AssumeRoleResponse;
import com.learning.ssm_swagger.entity.ali.STSToken;
import lombok.Getter;
import lombok.Setter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;
/**
* @Author Qizy
* @Date 2019/12/12 17:32
* @Version 1.0
**/
@Getter
@Setter
@Component
@Configuration
@PropertySource(value = { "classpath:sts.properties" })
public class STSUtil {
@Value("${sts.accessKeyId}")
public String accessKeyId;
@Value("${sts.accessKeySecret}")
public String accessKeySecret;
@Value("${sts.endpoint}")
public String endpoint;
@Value("${sts.roleArn}")
public String roleArn;
@Value("${sts.roleSessionName}")
public String roleSessionName;
public STSToken getSecurityToken(){
STSToken stsToken = new STSToken();
/*String policy = "{\n" +
" \"Version\": \"1\", \n" +
" \"Statement\": [\n" +
" {\n" +
" \"Action\": [\n" +
" \"oss:*\"\n" +
" ], \n" +
" \"Resource\": [\n" +
" \"acs:oss:*:*:*\" \n" +
" ], \n" +
" \"Effect\": \"Allow\"\n" +
" }\n" +
" ]\n" +
"}";*/
try {
// 添加endpoint(直接使用STS endpoint,前两个参数留空,无需添加region ID)
DefaultProfile.addEndpoint("", "", "Sts", endpoint);
// 构造default profile(参数留空,无需添加region ID)
IClientProfile profile = DefaultProfile.getProfile("", accessKeyId, accessKeySecret);
// 用profile构造client
DefaultAcsClient client = new DefaultAcsClient(profile);
final AssumeRoleRequest request = new AssumeRoleRequest();
request.setMethod(MethodType.POST);
request.setRoleArn(roleArn);
request.setRoleSessionName(roleSessionName);
// 若policy为空,则用户将获得该角色下所有权限
//request.setPolicy(policy);
// 设置凭证有效时间
request.setDurationSeconds(1000L);
final AssumeRoleResponse response = client.getAcsResponse(request);
System.out.println("Expiration: " + response.getCredentials().getExpiration());
System.out.println("Access Key Id: " + response.getCredentials().getAccessKeyId());
stsToken.setStsAccessKeyId(response.getCredentials().getAccessKeyId());
System.out.println("Access Key Secret: " + response.getCredentials().getAccessKeySecret());
stsToken.setStsAccessKeySecret(response.getCredentials().getAccessKeySecret());
System.out.println("Security Token: " + response.getCredentials().getSecurityToken());
stsToken.setSecurityToken(response.getCredentials().getSecurityToken());
System.out.println("RequestId: " + response.getRequestId());
} catch (ClientException e) {
e.printStackTrace();
}
return stsToken;
}
}
实体类
package com.learning.ssm_swagger.entity.ali;
/**
* @Author Qizy
* @Date 2019/12/13 12:02
* @Version 1.0
**/
public class STSToken {
private String securityToken="";
private String stsAccessKeyId="";
private String stsAccessKeySecret="";
public String getSecurityToken() {
return securityToken;
}
public void setSecurityToken(String securityToken) {
this.securityToken = securityToken;
}
public String getStsAccessKeyId() {
return stsAccessKeyId;
}
public void setStsAccessKeyId(String stsAccessKeyId) {
this.stsAccessKeyId = stsAccessKeyId;
}
public String getStsAccessKeySecret() {
return stsAccessKeySecret;
}
public void setStsAccessKeySecret(String stsAccessKeySecret) {
this.stsAccessKeySecret = stsAccessKeySecret;
}
}
OSSUtil.java 这个工具类中的initClient()方法实现了使用access密钥进行获取OSS实例。
相比于不使用sts的oss工具类,只需要修改initClient方法即可。
# OSSUtil.java
package com.learning.ssm_swagger.util;
import java.io.*;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import com.aliyun.oss.ClientException;
import com.aliyun.oss.OSSException;
import com.aliyun.oss.model.*;
import com.learning.ssm_swagger.entity.ali.STSToken;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;
import com.aliyun.oss.ClientConfiguration;
import com.aliyun.oss.OSSClient;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
@Component
@Configuration
@PropertySource(value = { "classpath:oss.properties" })
public class OSSUtil {
private static final org.slf4j.Logger logger = LoggerFactory.getLogger(OSSUtil.class);
private static final SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
@Value("${oss.accessKeyId}")
public String accessKeyId;
@Value("${oss.accessKeySecret}")
public String accessKeySecret;
@Value("${oss.endpoint}")
public String endpoint;
@Value("${oss.bucketName}")
public String bucketName;
private OSSClient client;
@Autowired
private STSUtil stsUtil;
/**************************************************************************
************************ oss对象相关 ****************************
*************************************************************************/
/**
* 初始化oss对象
* @return
*/
private OSSClient initClient() {
if (null == client) {
ClientConfiguration conf = new ClientConfiguration();
conf.setConnectionTimeout(5000);
conf.setMaxErrorRetry(10);
STSToken stsToken = stsUtil.getSecurityToken();
// conf.setProxyHost("127.0.0.1");
// conf.setProxyPort(8080);
client = new OSSClient(endpoint, stsToken.getStsAccessKeyId(), stsToken.getStsAccessKeySecret(), stsToken.getSecurityToken(), conf);
}
return client;
}
/**
* 获取OSS对象
* @return
*/
public OSSClient getClientObject(){
return initClient();
}
/**************************************************************************
************************ bucket相关 ****************************
*************************************************************************/
/**
* 创建bucket存储空间
* @param bucketName 存储空间名称
* @return Boolean
*/
public Boolean createBucket(String bucketName){
Boolean back = false;
OSSClient ossClient = null;
try {
ossClient = initClient();
//判断bucket是否存在
if(doesBucketExist(bucketName)){
return back;
}
// 创建CreateBucketRequest对象。
CreateBucketRequest createBucketRequest = new CreateBucketRequest(bucketName);
// 如果创建存储空间的同时需要指定存储类型以及数据容灾类型, 可以参考以下代码。
// 此处以设置存储空间的存储类型为标准存储为例。
// createBucketRequest.setStorageClass(StorageClass.Standard);
// 默认情况下,数据容灾类型为本地冗余存储,即DataRedundancyType.LRS。如果需要设置数据容灾类型为同城冗余存储,请替换为DataRedundancyType.ZRS。
// createBucketRequest.setDataRedundancyType(DataRedundancyType.ZRS)
// 创建存储空间。
ossClient.createBucket(createBucketRequest);
back = true;
} catch (OSSException oe) {
oe.printStackTrace();
return null;
} catch (ClientException ce) {
ce.printStackTrace();
return null;
} finally {
ossClient.shutdown();
client = null;
}
return back;
}
/**
* 删除指定的bucket
* @param bucketName 存储空间名称
* @return
*/
public Boolean deleteBucket(String bucketName){
Boolean back = false;
OSSClient ossClient = null;
try {
ossClient = initClient();
// 删除存储空间。
ossClient.deleteBucket(bucketName);
back = true;
} catch (OSSException oe) {
oe.printStackTrace();
return null;
} catch (ClientException ce) {
ce.printStackTrace();
return null;
} finally {
ossClient.shutdown();
client = null;
}
return back;
}
/**
* 判断存储空间是否存在
* @param bucketName 存储空间名称
* @return
*/
private Boolean doesBucketExist(String bucketName){
OSSClient ossClient = initClient();
return ossClient.doesBucketExist(bucketName);
}
/**
* 获取所有存储空间
* @return 存储空间集合
*/
public List<Bucket> getAllBuckets(){
OSSClient ossClient = null;
List<Bucket> back = new ArrayList<>();
try {
ossClient = initClient();
back = ossClient.listBuckets();
} catch (OSSException oe) {
oe.printStackTrace();
return null;
} catch (ClientException ce) {
ce.printStackTrace();
return null;
} finally {
ossClient.shutdown();
client = null;
}
return back;
}
/**
* 根据url获取bucketName
* @MethodName: getBucketName
* @Description: 根据url获取bucketName
* @param fileUrl 文件url
* @return String bucketName
*/
public String getBucketName(String fileUrl){
String http = "http://";
String https = "https://";
int httpIndex = fileUrl.indexOf(http);
int httpsIndex = fileUrl.indexOf(https);
int startIndex = 0;
if(httpIndex==-1){
if(httpsIndex==-1){
return null;
}else{
startIndex = httpsIndex+https.length();
}
}else{
startIndex = httpIndex+http.length();
}
int endIndex = fileUrl.indexOf(".oss-");
return fileUrl.substring(startIndex, endIndex);
}
/**************************************************************************
************************ 文件读取相关 **************************
*************************************************************************/
/**
* 上传文件到OSS 成功再返回的文件路径
* @MethodName: putObject
* @Description: 上传文件 底层方法:PutObjectRequest
* @param file
* @param fileType
* @param fileName
* @return String
*/
private String putObject(File file,String fileType,String fileName){
//默认null
String url = null;
OSSClient ossClient = null;
try {
ossClient = initClient();
InputStream input = new FileInputStream(file);
// 创建上传Object的Metadata
ObjectMetadata meta = new ObjectMetadata();
// 设置上传内容类型
meta.setContentType(contentType(fileType));
// 被下载时网页的缓存行为
meta.setCacheControl("no-cache");
//创建上传请求
PutObjectRequest request = new PutObjectRequest(bucketName, fileName,input,meta);
ossClient.putObject(request);
//上传成功再返回的文件路径
url = endpoint.replaceFirst("http://","http://"+bucketName+".")+"/"+fileName;
} catch (OSSException oe) {
oe.printStackTrace();
return null;
} catch (ClientException ce) {
ce.printStackTrace();
return null;
} catch (FileNotFoundException e) {
e.printStackTrace();
return null;
} finally {
ossClient.shutdown();
client = null;
}
return url;
}
/**
* OSS单文件上传
* @MethodName: uploadFile
* @Description: OSS单文件上传
* @param file
* @param fileType 文件后缀
* @return String 文件地址
*/
public String uploadFile(File file,String fileType){
//文件名,根据UUID来
String fileName = UUID.randomUUID().toString().toUpperCase().replace("-", "")+"."+fileType;
return putObject(file,fileType,fileName);
}
/**
*
* @MethodName: updateFile
* @Description: 更新文件:只更新内容,不更新文件名和文件地址。
* (因为地址没变,可能存在浏览器原数据缓存,不能及时加载新数据,例如图片更新,请注意)
* @param file
* @param fileType
* @param oldUrl
* @return String
*/
public String updateFile(File file,String fileType,String oldUrl){
String fileName = getFileName(oldUrl);
if(fileName==null) {
return null;
}
return putObject(file,fileType,fileName);
}
/**
*
* @MethodName: replaceFile
* @Description: 替换文件:删除原文件并上传新文件,文件名和地址同时替换
* 解决原数据缓存问题,只要更新了地址,就能重新加载数据)
* @param file
* @param fileType 文件后缀
* @param oldUrl 需要删除的文件地址
* @return String 文件地址
*/
public String replaceFile(File file,String fileType,String oldUrl){
//先删除原文件
boolean flag = deleteFile(oldUrl);
if(!flag){
//更改文件的过期时间,让他到期自动删除。
}
return uploadFile(file, fileType);
}
/**
* 根据url,单文件删除
* @MethodName: deleteFile
* @Description: 单文件删除 底层方法:DeleteObjectsRequest
* @param fileUrl 需要删除的文件url
* @return boolean 是否删除成功
*/
public boolean deleteFile(String fileUrl){
//根据url获取bucketName
String bucketName = getBucketName(fileUrl);
//根据url获取fileName
String fileName = getFileName(fileUrl);
if(bucketName==null||fileName==null) {
return false;
}
OSSClient ossClient = null;
try {
ossClient = initClient();
GenericRequest request = new DeleteObjectsRequest(bucketName).withKey(fileName);
ossClient.deleteObject(request);
} catch (Exception oe) {
oe.printStackTrace();
return false;
} finally {
ossClient.shutdown();
client = null;
}
return true;
}
/**
*
* @MethodName: batchDeleteFiles
* @Description: 批量文件删除(较慢):适用于不同endPoint和BucketName
* @param fileUrls 需要删除的文件url集合
* @return int 成功删除的个数
*/
public int deleteFiles(List<String> fileUrls){
int count = 0;
for (String url : fileUrls) {
if(deleteFile(url)){
count++;
}
}
return count;
}
/**
*
* @MethodName: batchDeleteFiles
* @Description: 批量文件删除(较快):适用于相同endPoint和BucketName
* @param fileUrls 需要删除的文件url集合
* @return int 成功删除的个数
*/
public int batchDeleteFiles(List<String> fileUrls){
//成功删除的个数
int deleteCount = 0;
//根据url获取bucketName
String bucketName = getBucketName(fileUrls.get(0));
//根据url获取fileName
List<String> fileNames = getFileName(fileUrls);
if(bucketName==null||fileNames.size()<=0) {
return 0;
}
OSSClient ossClient = null;
try {
ossClient = initClient();
DeleteObjectsRequest request = new DeleteObjectsRequest(bucketName).withKeys(fileNames);
DeleteObjectsResult result = ossClient.deleteObjects(request);
deleteCount = result.getDeletedObjects().size();
} catch (OSSException oe) {
oe.printStackTrace();
throw new RuntimeException("OSS服务异常:", oe);
} catch (ClientException ce) {
ce.printStackTrace();
throw new RuntimeException("OSS客户端异常:", ce);
} finally {
ossClient.shutdown();
client = null;
}
return deleteCount;
}
/**
* 根据url获取fileName
* @MethodName: getFileName
* @Description: 根据url获取fileName
* @param fileUrl 文件url
* @return String fileName
*/
private String getFileName(String fileUrl){
String str = "aliyuncs.com/";
int beginIndex = fileUrl.indexOf(str);
if(beginIndex==-1) {
return null;
}
return fileUrl.substring(beginIndex+str.length());
}
/**
* 根据url获取fileNames集合
* @MethodName: getFileName
* @Description: 根据url获取fileNames集合
* @param fileUrls 文件url集合
* @return List<String> fileName集合
*/
private List<String> getFileName(List<String> fileUrls){
List<String> names = new ArrayList<>();
for (String url : fileUrls) {
names.add(getFileName(url));
}
return names;
}
/**
* 获取文件类型
* @MethodName: contentType
* @Description: 获取文件类型
* @param fileType 文件类型
* @return String
*/
private String contentType(String fileType){
fileType = fileType.toLowerCase();
String contentType = "";
switch (fileType) {
case "bmp": contentType = "image/bmp";
break;
case "gif": contentType = "image/gif";
break;
case "png":
case "jpeg":
case "jpg": contentType = "image/jpeg";
break;
case "html":contentType = "text/html";
break;
case "txt": contentType = "text/plain";
break;
case "vsd": contentType = "application/vnd.visio";
break;
case "ppt":
case "pptx":contentType = "application/vnd.ms-powerpoint";
break;
case "doc":
case "docx":contentType = "application/msword";
break;
case "xml":contentType = "text/xml";
break;
case "mp4":contentType = "video/mp4";
break;
default: contentType = "application/octet-stream";
break;
}
return contentType;
}
/**
* 下载文件至本地
* @param fileUrl 文件oss路径url
* @param filePath 文件本地存储路径
* @return
*/
public boolean downLoadFile(String fileUrl,String filePath){
//根据url获取bucketName
String bucketName = getBucketName(fileUrl);
//根据url获取fileName
String fileName = getFileName(fileUrl);
if(bucketName==null||fileName==null) {
return false;
}
OSSClient ossClient = null;
try {
ossClient = initClient();
ossClient.getObject(new GetObjectRequest(bucketName, fileName), new File(filePath+fileName));
} catch (Exception oe) {
oe.printStackTrace();
return false;
} finally {
ossClient.shutdown();
client = null;
}
return true;
}
/**
* 读取文件内容
* @param fileUrl
* @return
*/
public Boolean readFile(String fileUrl){
//根据url获取bucketName
String bucketName = getBucketName(fileUrl);
//根据url获取fileName
String fileName = getFileName(fileUrl);
if(bucketName==null||fileName==null) {
return null;
}
OSSClient ossClient = null;
try {
ossClient = initClient();
OSSObject ossObject = ossClient.getObject(bucketName, fileName);
// 读取文件内容。
System.out.println("Object content:");
BufferedReader reader = new BufferedReader(new InputStreamReader(ossObject.getObjectContent()));
while (true) {
String line = reader.readLine();
if (line == null) {
break;
}
System.out.println("\n" + line);
}
} catch (Exception oe) {
oe.printStackTrace();
return false;
} finally {
ossClient.shutdown();
client = null;
}
return true;
}
}
简单的测试一下
# STSController.java
package com.learning.ssm_swagger.controller.ali;
import com.learning.ssm_swagger.util.OSSUtil;
import com.learning.ssm_swagger.util.STSUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import java.io.File;
/**
* @Author Qizy
* @Date 2019/12/11 9:43
* @Version 1.0
**/
@Controller
@RequestMapping("/sts")
@Api(value="STS Service Controller", description = "阿里云STS测试")
public class STSController {
private final org.slf4j.Logger logger = LoggerFactory.getLogger(getClass());
private static final String TO_PATH="upLoad";
private static final String RETURN_PATH="success";
@Autowired
private STSUtil stsUtil;
@ApiOperation(value = "STS-获取安全令牌", notes = "STS-获取安全令牌", httpMethod = "POST")
@RequestMapping(value = "/getScurityToken", method = RequestMethod.POST)
@ResponseBody
public String testUpload() throws Exception {
return stsUtil.getSecurityToken().getSecurityToken();
}
}
# OSSController.java 不需要修改
控制台结果打印
# console
Expiration: 2019-12-13T07:03:26Z
Access Key Id: STS.NSni************stTju
Access Key Secret: FBwmS7KzJkeJ******************6cpy4qdvJzcbA
Security Token: CAIS/wF1q6Ft5B2yf********************xCAkHQgbeFfm5HBlzz2IHlEfXJpAu0dsvU/n2tQ7voflr9vRoRZAFfYdo5r459K6wK9crbGuMGztQSRJgUiXDr9MQXy+eOPScebJYqvV5XAQlTAkTAJstmeXD6+XlujHISUgJp8FLo+VRW5ajw0b7U/ZHEVyqkgOGDWKOymPzPzn2PUFzAIgAdnjn5l4qnNqa/1qDim1QalkbJJ/9mvc8L7Ppk0ba0SCYnlgLZEEYPayzNV5hRw86N7sbdJ4z+vvKvGWgkIuEzdbrGIo400d1chOvUgebRNqf/njuF1ofDDPjZCDtuC7YsagAGUQMoQJylEAMLeLNAXo0+CO6ZH+xQEltnYlT/1tNtCb4iYayth4erFHOTXSHaqpn/8jUzyaOxQHAtBx5UV0a88ZWxsPCoYwB1JJjE+/dm+*****************************************************
RequestId: ADA**4E7-2**E-4**7-9**3-04E******9A4
来源:CSDN
作者:老难
链接:https://blog.csdn.net/qq_41561195/article/details/103664688