文章目录
本篇文章基于Springboot+Vue+Vant使用阿里云OSS图片上传功能,后端使用阿里云STS临时token安全认证。
准备工作
RAM相关
阿里云RAM官网:阿里云RAM官网
根据官网上的前四个步骤来操作 设置RAM子账号和自定义、配置权限和角色。
OSS相关
购买OSS服务
官网:阿里云OSS官网
创建一个oss bucket
创建名称和选择地区 这个地区的选择后面要用到
bucket权限控制
跨域设置
Springboot后端
工作原理:
所以我们只后端需要获取STS的凭证之后 前端拿到后端获取的STS凭证来上传阿里云图片
SDK官网下载
阿里云JAVASDK:阿里云JAVASDK官网
将sdk下载到本地 然后放到和pom文件夹同级目录下,可以去了解maven使用本地下载的sdk
在pom文件中加入:
<!-- 阿里云 SDK -->
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
<version>2.8.3</version>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-sts</artifactId>
<version>2.1.6</version>
<scope>system</scope>
<systemPath>${basedir}/lib/aliyun-java-sdk-sts-2.1.6.jar</systemPath>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-core</artifactId>
<version>2.1.7</version>
<scope>system</scope>
<systemPath>${basedir}/lib/aliyun-java-sdk-core-2.1.7.jar</systemPath>
</dependency>
<dependency>
<groupId>net.sf.json-lib</groupId>
<artifactId>json-lib</artifactId>
<version>2.4</version>
<scope>system</scope>
<systemPath>${basedir}/lib/json-lib-2.4-jdk15.jar</systemPath>
</dependency>
这个一定要打开
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<includeSystemScope>true</includeSystemScope>
</configuration>
</plugin>
STS获取
STSVO
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@ApiModel
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class STSVo {
@ApiModelProperty("host")
private String host;
@ApiModelProperty("ossAccessKeyId")
private String ossAccessKeyId;
@ApiModelProperty("accessKeySecret")
private String accessKeySecret;
@ApiModelProperty("signature")
private String signature;
@ApiModelProperty("policy")
private String policy;
@ApiModelProperty("userPath")
private String userPath;
@ApiModelProperty("securityToken")
private String securityToken;
}
AliyunSTSService(获取STS临时token)
官网第五步:阿里云STS临时token
这里对应官网的步骤五 对应的api数据看官网的如何获取 只需要替换如下代码中的
1、roleArn
2、getAccessKey
3、accessKeySecret
4、policy
5、getHost 你的oss地址
import java.util.ArrayList;
import java.util.Base64;
import java.util.HashMap;
import java.util.List;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.http.MethodType;
import com.aliyuncs.http.ProtocolType;
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.google.gson.Gson;
import lombok.AllArgsConstructor;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import org.springframework.stereotype.Service;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.TimeZone;
@Service
@AllArgsConstructor
public class AliyunSTSService {
/**
*
*/
// 目前只有"cn-shanghai"这个region可用, 不要使用填写其他region的值
public static final String REGION_CN_BEIJING = "cn-beijing";
public static final String ENDPOINT = "sts.cn-beijing.aliyuncs.com";
public static final String STS_API_VERSION = "2015-04-01";
protected AssumeRoleResponse assumeRole(String accessKeyId, String accessKeySecret, String roleArn,
String roleSessionName, String policy, ProtocolType protocolType, long durationSeconds) throws ClientException
{
try {
DefaultProfile.addEndpoint("",REGION_CN_BEIJING, "Sts", ENDPOINT);;
// 创建一个 Aliyun Acs Client, 用于发起 OpenAPI 请求
IClientProfile profile = DefaultProfile.getProfile(REGION_CN_BEIJING, accessKeyId, accessKeySecret);
DefaultAcsClient client = new DefaultAcsClient(profile);
// 创建一个 AssumeRoleRequest 并设置请求参数
final AssumeRoleRequest request = new AssumeRoleRequest();
request.setVersion(STS_API_VERSION);
request.setMethod(MethodType.POST);
request.setProtocol(protocolType);
request.setRoleArn(roleArn);
request.setRoleSessionName(roleSessionName);
request.setPolicy(policy);
request.setDurationSeconds(durationSeconds);
// 发起请求,并得到response
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());
return response;
} catch (ClientException e) {
throw e;
}
}
public String getAccessKey() {
//非主账号的ram的accessKeySecret
return "LTAI5*****DDFt9ntw";
}
public AssumeRoleResponse getSTS() {
String accessKeyId = getAccessKey();
//非主账号的ram的accessKeySecret
String accessKeySecret = "APNDvh*****mDoJa13X";
String roleArn = "acs:ram::113*********4527:role/ossram";
// 自定义角色会话名称
String roleSessionName = "ramoss";
//角色权限。 这个权限要小于等于你创建ram的权限
String policy = "{\n" +
" \"Version\": \"1\",\n" +
" \"Statement\": [\n" +
" {\n" +
" \"Effect\": \"Allow\",\n" +
" \"Action\": \"oss:PutObject\",\n" +
//你自己的oss地址
" \"Resource\": \"acs:oss:*:*:w********/*\"\n" +
" }\n" +
" ]\n" +
"}";
long durationSeconds = 3600L;
try {
return assumeRole(accessKeyId, accessKeySecret, roleArn, roleSessionName, policy, ProtocolType.HTTPS, durationSeconds);
} catch (ClientException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return null;
}
}
public String getPolicyBase64(AssumeRoleResponse response) {
HashMap<String, Object> policyMap = new HashMap<String, Object>();
int timeout = 1; // 设置过期时间为2小时
// 获取当前时间,并加上过期时间
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.HOUR_OF_DAY, timeout);
// 转换为ISO格式字符串
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
String expiration = dateFormat.format(calendar.getTime());
policyMap.put("expiration", response.getCredentials().getExpiration());
/**
*
{
"expiration":"2023-05-26T17:33:34Z",
"conditions":[
[
"content-length-range",
0,
1048576
]
]
}
*/
List<Object> conditionArray = new ArrayList<Object>();
List<Object> contentLengthArray = new ArrayList<Object>();
contentLengthArray.add("content-length-range");
contentLengthArray.add(0);
contentLengthArray.add(5242880); // 5M
conditionArray.add(contentLengthArray);
policyMap.put("conditions", conditionArray);
Gson gson = new Gson();
String gsonString = gson.toJson(policyMap);
String encodedStr = Base64.getEncoder().encodeToString(gsonString.getBytes());
return encodedStr;
}
public String getSignature(String policyBase64, AssumeRoleResponse response) throws Exception {
Mac hmac = Mac.getInstance("HmacSHA1");
String accessKeySecret = response.getCredentials().getAccessKeySecret();
SecretKeySpec keySpec = new SecretKeySpec(accessKeySecret.getBytes(StandardCharsets.UTF_8), "HmacSHA1");
hmac.init(keySpec);
byte[] hmacBytes = hmac.doFinal(policyBase64.getBytes(StandardCharsets.UTF_8));
return Base64.getEncoder().encodeToString(hmacBytes);
}
public String getHost() {
//你自己的oss地址
return "https://w*******.oss-cn-beijing.aliyuncs.com";
}
public String getUserPath() {
//path是唯一 自己定义
String path="";
return "user/" +path
}
}
AliyunController
import com.aliyuncs.sts.model.v20150401.AssumeRoleResponse;
import demo.example.demo.service.AliyunSTSService;
import demo.example.demo.utils.ApiResponse;
import demo.example.demo.vo.STSVo;
import io.swagger.annotations.ApiOperation;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
//
@RestController
@RequestMapping("/api/aliyun")
@AllArgsConstructor
public class AliyunController {
private AliyunSTSService aliyunSTS;
@ApiOperation(value = "sts")
@GetMapping("/sts")
public ApiResponse<STSVo> getSTSInfo(){
AssumeRoleResponse assumeRoleResponse = aliyunSTS.getSTS();
String policy = aliyunSTS.getPolicyBase64(assumeRoleResponse);
String signature;
try {
signature = aliyunSTS.getSignature(policy, assumeRoleResponse);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
return ApiResponse.fail("签名获取失败", null);
}
String path = "wang";
STSVo vo = STSVo.builder()
.host(aliyunSTS.getHost())
.ossAccessKeyId(assumeRoleResponse.getCredentials().getAccessKeyId())
.signature(signature)
.accessKeySecret(assumeRoleResponse.getCredentials().getAccessKeySecret())
.policy(policy)
.userPath(path)
.securityToken(assumeRoleResponse.getCredentials().getSecurityToken())
.build();
return ApiResponse.success(vo, "签名获取成功");
}
}
Vue前端
vue这里我使用的是vant组件,elementUI组件上传方式一样,只不过两种组件的上传组件的函数调用返回值不一样 可以去官网了解
vant官网:van2官网
阿里云的oss模块
npm i ali-oss
uploader组件
<van-uploader
:show-file-list="false"
:after-read="fnUploadRequest"
>
<van-image
style="border: thin solid #000000;"
v-if="this.user.photo"
:src="this.user.photo"
width="4rem"
height="4rem"
/>
</van-uploader>
JS部分
getSts() {
this.request.get("/api/aliyun/sts").then((res) => {
console.log(res);
this.sts = res.data;
});
},
fnUploadRequest(file) {
// console.log(file);
if (
this.sts.ossAccessKeyId != null &&
this.sts.ossAccessKeyId != undefined
) {
const client = new OSS({
region: "oss-cn-beijing",
accessKeyId: this.sts.ossAccessKeyId,
accessKeySecret: this.sts.accessKeySecret,
stsToken: this.sts.securityToken,
bucket: "wyh*****",
secure: true,
});
file.status = "uploading";
file.message = "上传中...";
var len = len || 16;
var $chars = "ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678";
var maxPos = $chars.length;
var pwd = "";
for (let i = 0; i < len; i++) {
pwd += $chars.charAt(Math.floor(Math.random() * maxPos)); // 生成16位随机字符
}
let name = file.file.name;
var pos = name.indexOf(".");
let type = name.substr(pos);
//accompany是我oss的文件路径
var fileName = "accompany/" + `${Date.parse(new Date())}` + pwd + type; // 文件名称(时间戳+随机字符+后缀)
client
.multipartUpload(fileName, file.file)
.then((res) => {
// console.log("res", res.res.requestUrls[0]);
let imageUrlObj = {};
imageUrlObj.fileName = res.res.requestUrls[0];
imageUrlObj.fileUrl = res.res.requestUrls[0];
imageUrlObj.fileType = 1;
imageUrlObj.type = 1;
console.log(imageUrlObj);
this.user.photo = imageUrlObj.fileUrl.split("?uploadId")[0];
//你的业务
file.status = "";
file.message = "";
})
.catch((err) => {
console.log(err);
});
console.log(file);
} else {
Toast.fail("上传失败");
return;
}
},