1. 创建用户配置权限
参考: 什么是STS 阿里云创建用户及授权参考: 使用STS临时访问凭证访问OSS
创建完成后我们会获得:
- 用户AccessKey ID
- 用户AccessKey Secret
- 角色ARN
2.创建存储空间
参考: 创建存储空间 创建完成后我们会获得:
- BucketName
- Region
- Endpoint
注意: 4. 预览时如果想在浏览器中预览,在Bucket中->传输管理->域名管理中绑定域名即可 5. Bucket读写权限设置为私有即可 6. 跨域设置 2.1. 来源:* 2.2. 允许Methods全勾选 2.3. 允许Headers:* 2.4. 暴露Headers可以不写 *参考
3. 后端项目整合
3.1 ruoyi-common/pom.xml添加依赖
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
<version>3.10.2</version>
</dependency>
3.2 application.yml添加配置
oss:
sts:
accesskey:
id: 第一步用户AccessKey ID
secret: 第一步用户AccessKey Secret
bucket: 第二步BucketName
region: 第二步Region
endpoint: 第二步Endpoint
role:
arn: 第一步角色ARN
- 之前的文章整合cas时有拆分了开发、测试、生产库的配置文件,那么在其中添加,如果没有,那么直接添加进application.yml中
oss:
sts:
callbackUrl: 该应用对外的域名[端口],如:https://dky12.xxx.com
3.3 com.ruoyi.common.utils.oss添加工具类
package com.ruoyi.common.utils.oss;
import com.alibaba.fastjson.JSONObject;
import com.aliyun.oss.HttpMethod;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.model.GeneratePresignedUrlRequest;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.auth.sts.AssumeRoleRequest;
import com.aliyuncs.auth.sts.AssumeRoleResponse;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.profile.DefaultProfile;
import com.aliyuncs.profile.IClientProfile;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.CollectionUtils;
import java.net.URL;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
public class AliyunStsOssUtil {
public static Logger logger = LoggerFactory.getLogger(AliyunStsOssUtil.class);
public static final String ACCESS_KEY_ID = "ACCESS_KEY_ID";
public static final String ACCESS_KEY_SECRET = "ACCESS_KEY_SECRET";
public static final String SECURITY_TOKEN = "SECURITY_TOKEN";
public static final String EXPIRATION = "EXPIRATION";
private static final String REGION = "cn-beijing";
private static final String STS_API_VERSION = "2015-04-01";
private static final String OWN_DOMAIN_NAME = "bucket中绑定的域名";
public static JSONObject getCredit(String userName, String roleArn, String accessKeyId, String accessKeySecret, String bucketName) throws ClientException {
String roleSessionName = userName;
IClientProfile profile = DefaultProfile.getProfile(REGION, accessKeyId, accessKeySecret);
DefaultAcsClient client = new DefaultAcsClient(profile);
final AssumeRoleRequest request = new AssumeRoleRequest();
request.setVersion(STS_API_VERSION);
request.setRoleArn(roleArn);
request.setRoleSessionName(roleSessionName);
request.setDurationSeconds(900L);
final AssumeRoleResponse response = client.getAcsResponse(request);
JSONObject jsonObject = new JSONObject();
jsonObject.put(ACCESS_KEY_ID, response.getCredentials().getAccessKeyId());
jsonObject.put(ACCESS_KEY_SECRET, response.getCredentials().getAccessKeySecret());
jsonObject.put(SECURITY_TOKEN, response.getCredentials().getSecurityToken());
jsonObject.put(EXPIRATION, response.getCredentials().getExpiration());
return jsonObject;
}
public static String getStsUrl(String userName, String roleArn, String accessKeyId, String accessKeySecret, String bucketName, String endpoint, String objectName)throws ClientException {
OSS ossClient = getSTSOssClient(userName, roleArn, accessKeyId, accessKeySecret, bucketName, endpoint);
Date expiration = null;
expiration = getExpiration(expiration);
GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(bucketName, objectName, HttpMethod.GET);
request.setExpiration(expiration);
URL signedUrl = ossClient.generatePresignedUrl(request);
ossClient.shutdown();
return signedUrl.toString().replaceFirst( bucketName+ "." +endpoint,OWN_DOMAIN_NAME);
}
public static List<String> getStsUrl(String userName, String roleArn, String accessKeyId, String accessKeySecret, String bucketName, String endpoint, List<String> objectNames)throws ClientException {
List<String> urls = new ArrayList<>();
if(!CollectionUtils.isEmpty(objectNames)){
OSS ossClient = getSTSOssClient(userName, roleArn, accessKeyId, accessKeySecret, bucketName, endpoint);
objectNames.forEach(objectName -> {
Date expiration = null;
expiration = getExpiration(expiration);
GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(bucketName, objectName, HttpMethod.GET);
request.setExpiration(expiration);
URL signedUrl = ossClient.generatePresignedUrl(request);
urls.add(signedUrl.toString().replaceFirst( bucketName+ "." +endpoint,OWN_DOMAIN_NAME));
});
ossClient.shutdown();
}
return urls;
}
private static Date getExpiration(Date expiration) {
try {
long nowTimeMillis = System.currentTimeMillis();
nowTimeMillis = nowTimeMillis + ( 600 * 1000);
expiration = new Date(nowTimeMillis);
} catch (Exception e) {
e.printStackTrace();
}
return expiration;
}
private static OSS getSTSOssClient(String userName, String roleArn, String accessKeyId, String accessKeySecret, String bucketName, String endpoint) throws ClientException {
JSONObject credit = getCredit(userName, roleArn, accessKeyId, accessKeySecret, bucketName);
String stsAccessKeyId = credit.getString(ACCESS_KEY_ID);
String stsAccessKeySecret = credit.getString(ACCESS_KEY_SECRET);
String securityToken = credit.getString(SECURITY_TOKEN);
return new OSSClientBuilder().build(endpoint, stsAccessKeyId, stsAccessKeySecret, securityToken);
}
}
3.4 com.ruoyi.aliyun.oss.controller添加OssController
package com.ruoyi.aliyun.oss.controller;
import com.alibaba.fastjson.JSONObject;
import com.aliyun.oss.common.utils.BinaryUtil;
import com.aliyuncs.exceptions.ClientException;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.oss.AliyunStsOssUtil;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URI;
import java.net.URLDecoder;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.spec.X509EncodedKeySpec;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
@RestController
@RequestMapping("/oss")
public class OssController {
private Logger logger = LoggerFactory.getLogger(this.getClass());
@Value("${oss.sts.endpoint}")
private String endpoint;
@Value("${oss.sts.region}")
private String ossRegion;
@Value("${oss.sts.bucket}")
private String ossBucket;
@Value("${oss.sts.accesskey.id}")
private String ossAccessKeyId;
@Value("${oss.sts.accesskey.secret}")
private String ossAccessKeySecret;
@Value("${oss.sts.role.arn}")
private String ossRoleArn;
@Value("${oss.sts.callbackUrl}")
private String callbackUrl;
@RequestMapping("/getCredit")
public JSONObject getCredit(String type) throws ClientException {
JSONObject jsonObject = new JSONObject();
JSONObject creditInfo;
creditInfo = AliyunStsOssUtil.getCredit("xxx", ossRoleArn, ossAccessKeyId, ossAccessKeySecret, ossBucket);
jsonObject.put("region", ossRegion);
jsonObject.put("AccessKeyId", creditInfo.getString(AliyunStsOssUtil.ACCESS_KEY_ID));
jsonObject.put("AccessKeySecret", creditInfo.getString(AliyunStsOssUtil.ACCESS_KEY_SECRET));
jsonObject.put("SecurityToken", creditInfo.getString(AliyunStsOssUtil.SECURITY_TOKEN));
jsonObject.put("Expiration", creditInfo.getString(AliyunStsOssUtil.EXPIRATION));
jsonObject.put("bucket", this.ossBucket);
jsonObject.put("StatusCode", 200);
String callbackUrl = this.callbackUrl + "/oss/callback";
JSONObject jasonCallback = new JSONObject();
jasonCallback.put("url", callbackUrl);
jasonCallback.put("body",
"filename=${object}" +
"&size=${size}" +
"&mimeType=${mimeType}" +
"&height=${imageInfo.height}" +
"&width=${imageInfo.width}" +
"&fileOriginalName=${x:fileOriginalName}" +
"&uploadUserId=${x:uploadUserId}" +
"&funCode=${x:funCode}" +
"&dataId=${x:dataId}" +
"&newFileName=${x:newFileName}" +
"&suffixName=${x:suffixName}");
jsonObject.put("callback", jasonCallback);
return jsonObject;
}
@PostMapping("/callback")
public void callback(HttpServletRequest request, HttpServletResponse response) throws IOException {
String ossCallbackBody = GetPostBody(request.getInputStream(),
Integer.parseInt(request.getHeader("content-length")));
boolean ret = VerifyOSSCallbackRequest(request, ossCallbackBody);
logger.debug("ossCallbackBody :" + ossCallbackBody);
logger.debug("verify result : " + ret);
if (ret) {
if(i == 1){
response(request, response, "{\"Status\":\"OK\"}", HttpServletResponse.SC_OK);
}else {
response(request, response, "{\"Status\":\"verdify not ok\"}", HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
}
} else {
response(request, response, "{\"Status\":\"verdify not ok\"}", HttpServletResponse.SC_BAD_REQUEST);
}
}
private void response(HttpServletRequest request, HttpServletResponse response, String results, int status)
throws IOException {
String callbackFunName = request.getParameter("callback");
response.addHeader("Content-Length", String.valueOf(results.length()));
if (callbackFunName == null || callbackFunName.equalsIgnoreCase(""))
response.getWriter().println(results);
else
response.getWriter().println(callbackFunName + "( " + results + " )");
response.setStatus(status);
response.flushBuffer();
}
private String GetPostBody(InputStream is, int contentLen) {
if (contentLen > 0) {
int readLen = 0;
int readLengthThisTime = 0;
byte[] message = new byte[contentLen];
try {
while (readLen != contentLen) {
readLengthThisTime = is.read(message, readLen, contentLen - readLen);
if (readLengthThisTime == -1) {
break;
}
readLen += readLengthThisTime;
}
return new String(message);
} catch (IOException e) {
}
}
return "";
}
protected boolean VerifyOSSCallbackRequest(HttpServletRequest request, String ossCallbackBody)
throws NumberFormatException, IOException {
boolean ret = false;
String autorizationInput = new String(request.getHeader("Authorization"));
String pubKeyInput = request.getHeader("x-oss-pub-key-url");
byte[] authorization = BinaryUtil.fromBase64String(autorizationInput);
byte[] pubKey = BinaryUtil.fromBase64String(pubKeyInput);
String pubKeyAddr = new String(pubKey);
if (!pubKeyAddr.startsWith("http://gosspublic.alicdn.com/")
&& !pubKeyAddr.startsWith("https://gosspublic.alicdn.com/")) {
System.out.println("pub key addr must be oss addrss");
return false;
}
String retString = executeGet(pubKeyAddr);
retString = retString.replace("-----BEGIN PUBLIC KEY-----", "");
retString = retString.replace("-----END PUBLIC KEY-----", "");
String queryString = request.getQueryString();
String uri = request.getRequestURI();
String decodeUri = java.net.URLDecoder.decode(uri, "UTF-8");
String authStr = decodeUri;
if (queryString != null && !queryString.equals("")) {
authStr += "?" + queryString;
}
authStr += "\n" + ossCallbackBody;
ret = doCheck(authStr, authorization, retString);
logger.info("------authStr:" + authStr);
logger.info("------pubKeyAddr:" + pubKeyAddr);
logger.info("------authorization:" + authorization);
logger.info("------retString:" + retString);
return ret;
}
@SuppressWarnings({"finally"})
private String executeGet(String url) {
BufferedReader in = null;
String content = null;
try {
CloseableHttpClient client = HttpClientBuilder.create().build();
HttpGet request = new HttpGet();
request.setURI(new URI(url));
HttpResponse response = client.execute(request);
in = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
StringBuffer sb = new StringBuffer("");
String line = "";
String NL = System.getProperty("line.separator");
while ((line = in.readLine()) != null) {
sb.append(line + NL);
}
in.close();
content = sb.toString();
} catch (Exception e) {
} finally {
if (in != null) {
try {
in.close();
} catch (Exception e) {
e.printStackTrace();
}
}
return content;
}
}
private static boolean doCheck(String content, byte[] sign, String publicKey) {
try {
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
byte[] encodedKey = BinaryUtil.fromBase64String(publicKey);
PublicKey pubKey = keyFactory.generatePublic(new X509EncodedKeySpec(encodedKey));
java.security.Signature signature = java.security.Signature.getInstance("MD5withRSA");
signature.initVerify(pubKey);
signature.update(content.getBytes());
boolean bverify = signature.verify(sign);
return bverify;
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
private Map<String, String> getUrlParams(String ossCallbackBody) throws UnsupportedEncodingException {
Map<String, String> params = new HashMap<>();
String[] split = ossCallbackBody.replaceAll("\"","").split("&");
for (String s : split) {
String[] split1 = s.split("=");
if (split1.length == 2) {
params.put(split1[0], URLDecoder.decode(split1[1], "utf-8"));
}else {
params.put(split1[0],"");
}
}
return params;
}
}
3.5 callback接口配置权限
com.ruoyi.framework.config.CasSecurityConfig类中configure方法添加:
.antMatchers("/oss/callback").permitAll()
4. 前端项目整合
4.1 安装依赖
npm install ali-oss -s
4.2 vuex添加用户id
src\store\modules\user.js中 state属性添加
userId: '',
mutations属性添加
SET_ID: (state, userId) => {
state.userId = userId
},
actions属性的GetInfo函数修改
GetInfo({ commit, state }) {
return new Promise((resolve, reject) => {
getInfo(state.token).then(res => {
const user = res.user
const avatar = user.avatar == "" ? require("@/assets/images/profile.jpg") : process.env.VUE_APP_BASE_API + user.avatar;
if (res.roles && res.roles.length > 0) {
commit('SET_ROLES', res.roles)
commit('SET_PERMISSIONS', res.permissions)
} else {
commit('SET_ROLES', ['ROLE_DEFAULT'])
}
commit('SET_NAME', user.userName)
commit('SET_ID', user.userId)
commit('SET_AVATAR', avatar)
resolve(res)
}).catch(error => {
reject(error)
})
})
},
src\store\getters.js中getters属性添加
userId: state => state.user.userId,
4.3 添加src\api\oss\oss.js
import request from '@/utils/request'
import OSS from 'ali-oss';
import store from '@/store'
const getClient = async option => {
let credit = await request({
url:'/oss/getCredit',
method: 'get'
});
console.log(option)
return {
client : new OSS({
region: credit.region,
accessKeyId: credit.AccessKeyId,
accessKeySecret: credit.AccessKeySecret,
stsToken: credit.SecurityToken,
bucket: credit.bucket,
secure: true,
}),
callback : credit.callback,
};
}
const uploadAction = async option => {
let {client, callback} = await getClient();
console.log(option)
let filePath = '/' + option.fileName;
filePath = filePath.replace(/[\/]+/g, '\/');
console.log("uploadUserId=>",store.getters.userId)
callback.customValue = {
uploadUserId: store.getters.userId + '',
funCode: option.funCode,
dataId: option.dataId,
fileOriginalName: option.file.name,
newFileName: option.newFileName,
suffixName: option.suffixName,
};
callback.contentType = "application/json";
return client.put(
filePath,
option.file, {
callback: callback,
}
)
}
const uploadFiles = async (files, fileDir, funCode, dataId, progress = console.log) => {
console.log("files=>",files)
let urls = [];
let date = new Date();
let year = date.getFullYear();
let month = date.getMonth() + 1;
let day = date.getDate();
for (let index = 0; index < files.length; index++) {
let file = files[index];
let suffixNameIndex = file.name.lastIndexOf('.');
let suffixName = suffixNameIndex == -1 ? '' : file.name.substring(suffixNameIndex);
let newFileName = uuid().replaceAll("-","").toLocaleLowerCase() + suffixName
let res = await uploadAction({
file: files[index],
fileName: `${fileDir}/${year}/${month}/${day}/` + newFileName,
progress: progress,
funCode: funCode,
dataId: dataId + '',
newFileName,
suffixName: suffixName.length < 1 ? '': suffixName.substr(1),
});
if (res.name) {
urls.push(`${res.name}`);
}
}
return urls;
}
const downloadFile = async (filePath, filename)=>{
let {client, callback} = await getClient();
const response = {
'content-disposition': `attachment; filename=${encodeURIComponent(filename)}`
}
const url = client.signatureUrl(filePath, { response });
console.log(url);
return url;
}
function uuid(len, radix) {
var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split('');
var uuid = [], i;
radix = radix || chars.length;
if (len) {
for (i = 0; i < len; i++) uuid[i] = chars[0 | Math.random()*radix];
} else {
var r;
uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-';
uuid[14] = '4';
for (i = 0; i < 36; i++) {
if (!uuid[i]) {
r = 0 | Math.random()*16;
uuid[i] = chars[(i == 19) ? (r & 0x3) | 0x8 : r];
}
}
}
return uuid.join('');
}
export default {
upload: uploadFiles,
getClient: getClient,
download: downloadFile,
}
4.4 全局挂载oss.js
src\main.js中添加
import oss from './api/oss/oss'
Vue.prototype.$oss = oss;
*注意事项
- 前端上传回调签名校验失败
|