ruoyi-file 增加 获取加签的url (getPresignedUrl)

pull/97/head
duandazhi 4 years ago
parent 1e4ed04a65
commit 997fab4991

@ -2,7 +2,9 @@ package com.ruoyi.system.api;
import org.springframework.cloud.openfeign.FeignClient; import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RequestPart; import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import com.ruoyi.common.core.constant.ServiceNameConstants; import com.ruoyi.common.core.constant.ServiceNameConstants;
@ -26,4 +28,11 @@ public interface RemoteFileService
*/ */
@PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) @PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public R<SysFile> upload(@RequestPart(value = "file") MultipartFile file); public R<SysFile> upload(@RequestPart(value = "file") MultipartFile file);
/**
* url; url,url
* @param fileUrl url
*/
@GetMapping("getPresignedUrl")
R<String> getPresignedUrl(@RequestParam(value = "fileUrl") String fileUrl);
} }

@ -30,6 +30,11 @@ public class RemoteFileFallbackFactory implements FallbackFactory<RemoteFileServ
{ {
return R.fail("上传文件失败:" + throwable.getMessage()); return R.fail("上传文件失败:" + throwable.getMessage());
} }
@Override
public R<String> getPresignedUrl(String fileUrl) {
return R.fail("文件加签失败:" + throwable.getMessage());
}
}; };
} }
} }

@ -46,6 +46,12 @@
<groupId>com.github.tobato</groupId> <groupId>com.github.tobato</groupId>
<artifactId>fastdfs-client</artifactId> <artifactId>fastdfs-client</artifactId>
</dependency> </dependency>
<!-- FastDFS 主要提供 【 ProtoCommon.getToken】 这个一个方法 -->
<dependency>
<groupId>net.oschina.zcx7878</groupId>
<artifactId>fastdfs-client-java</artifactId>
<version>1.27.0.0</version>
</dependency>
<!-- Minio --> <!-- Minio -->
<dependency> <dependency>

@ -0,0 +1,53 @@
package com.ruoyi.file.config;
import com.github.tobato.fastdfs.FdfsClientConstants;
import com.github.tobato.fastdfs.domain.conn.PooledConnectionFactory;
import com.ruoyi.file.service.FastDfsSysFileServiceImpl;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.context.annotation.Configuration;
/**
* fastdfs
* @author dazer
* @see FastDfsSysFileServiceImpl
* FastDFS {@link PooledConnectionFactory}
*
* 使: DockerFastDFS https://www.cnblogs.com/cao-lei/p/13470695.html
* github https://github.com/tobato/FastDFS_Client
*/
@RefreshScope
@Configuration
@ConfigurationProperties(
prefix = FdfsClientConstants.ROOT_CONFIG_PREFIX
)
public class FastDfsConfig {
/**
* 访or ip
* FastDFS {@link PooledConnectionFactory}
* //@Value("${fdfs.domain}")
*/
private String domain;
/**
* tokenkey
* fastdfs FastDFS etc/fdfs/http.conf http.anti_steal.secret_key
*/
private String tokenSecretKey;
public String getDomain() {
return domain;
}
public void setDomain(String domain) {
this.domain = domain;
}
public String getTokenSecretKey() {
return tokenSecretKey;
}
public void setTokenSecretKey(String tokenSecretKey) {
this.tokenSecretKey = tokenSecretKey;
}
}

@ -1,6 +1,7 @@
package com.ruoyi.file.config; package com.ruoyi.file.config;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import io.minio.MinioClient; import io.minio.MinioClient;
@ -10,6 +11,7 @@ import io.minio.MinioClient;
* *
* @author ruoyi * @author ruoyi
*/ */
@RefreshScope
@Configuration @Configuration
@ConfigurationProperties(prefix = "minio") @ConfigurationProperties(prefix = "minio")
public class MinioConfig public class MinioConfig
@ -34,6 +36,30 @@ public class MinioConfig
*/ */
private String bucketName; private String bucketName;
/**
* 访; url访ip
* eg: https://yq666.bj.gov.cn/appt-file
*
* minio Bucket Policy Bucket ==> Edit Bucket
* 1Read Only
* 2Write Only
* 3Read and Write
* 4 访
*/
private String domain;
/**
*
* MinIO STS http://docs.minio.org.cn/docs/master/minio-sts-quickstart-guide
* Amazon S3MinIO Java SDK: API: Presigned: presignedGetObject: http://docs.minio.org.cn/docs/master/java-client-quickstart-guide
* 7
* 13600 = 60 * 60 * 1
* 24186400 = 60 * 60 * 24
* 7604800 = 86400 * 7
* -1 url
*/
private Integer expiryDuration = 86400;
public String getUrl() public String getUrl()
{ {
return url; return url;
@ -74,6 +100,31 @@ public class MinioConfig
this.bucketName = bucketName; this.bucketName = bucketName;
} }
public String getDomain() {
return domain;
}
public void setDomain(String domain) {
this.domain = domain;
}
public Integer getExpiryDuration() {
if (expiryDuration == null) {
// 默认一个小时, 3600秒
expiryDuration = 86400;
}
if (expiryDuration < 1L && expiryDuration != -1) {
// 最小1秒
// 如果要永不过期,就不要调用 -1 直接原样返回
expiryDuration = 1;
}
return expiryDuration;
}
public void setExpiryDuration(Integer expiryDuration) {
this.expiryDuration = expiryDuration;
}
@Bean @Bean
public MinioClient getMinioClient() public MinioClient getMinioClient()
{ {

@ -2,6 +2,7 @@ package com.ruoyi.file.config;
import java.io.File; import java.io.File;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry; import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
@ -12,6 +13,7 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
* *
* @author ruoyi * @author ruoyi
*/ */
@RefreshScope
@Configuration @Configuration
public class ResourcesConfig implements WebMvcConfigurer public class ResourcesConfig implements WebMvcConfigurer
{ {

@ -1,9 +1,13 @@
package com.ruoyi.file.controller; package com.ruoyi.file.controller;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import com.ruoyi.common.core.domain.R; import com.ruoyi.common.core.domain.R;
@ -45,4 +49,19 @@ public class SysFileController
return R.fail(e.getMessage()); return R.fail(e.getMessage());
} }
} }
/**
* 访url
* AWS Security Token Service (STS) (federation token)
* aliyun oss : http://react-yuebaoxiao-pro.oss-cn-shanghai.aliyuncs.com/dev/upload/default/20210719-23d31398-4849-408d-8775-a5b668ccafc3.jpeg?Expires=1626736182&OSSAccessKeyId=LTAI4GDQSbwgmbsRxxbDXnKT&Signature=P3w3%2FIpEnZEUhYku6scOos4p54A%3D
* minio : https://yq666.bj.gov.cn/appt-file/dev/default/2021/07/19/5fe1478b-969c-4b6e-9cc0-742412dc3128.jpeg?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=D99KGE6ZTQXSATTJWU24%2F20210719%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20210719T112025Z&X-Amz-Expires=432000&X-Amz-SignedHeaders=host&X-Amz-Signature=e45171d0885f006ee1de43cec9d88963e2b55c6e671740ae5695410ba16770c5
* ---------------------------------------------------
* 访 bucket 访
*/
@ApiOperation(value = "临时安全凭证、获取加签的url", notes = "根据输入的url,获取带有临时安全凭证的url")
@GetMapping("getPresignedUrl")
public R<String> getPresignedUrl(
@ApiParam("需要访问的url,字段名:fileUrl,必填;不要带有?后面的参数") @RequestParam(value = "fileUrl") String fileUrl) {
return R.ok(sysFileService.presignedUrl(fileUrl), "获取成功");
}
} }

@ -1,8 +1,12 @@
package com.ruoyi.file.service; package com.ruoyi.file.service;
import com.ruoyi.common.core.constant.HttpStatus;
import com.ruoyi.common.core.exception.file.FileException;
import com.ruoyi.file.config.FastDfsConfig;
import org.apache.commons.io.FilenameUtils; import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import org.csource.fastdfs.ProtoCommon;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import com.github.tobato.fastdfs.domain.fdfs.StorePath; import com.github.tobato.fastdfs.domain.fdfs.StorePath;
@ -16,11 +20,8 @@ import com.github.tobato.fastdfs.service.FastFileStorageClient;
@Service @Service
public class FastDfsSysFileServiceImpl implements ISysFileService public class FastDfsSysFileServiceImpl implements ISysFileService
{ {
/** @Autowired
* 访 private FastDfsConfig fastDfsConfig;
*/
@Value("${fdfs.domain}")
public String domain;
@Autowired @Autowired
private FastFileStorageClient storageClient; private FastFileStorageClient storageClient;
@ -37,6 +38,33 @@ public class FastDfsSysFileServiceImpl implements ISysFileService
{ {
StorePath storePath = storageClient.uploadFile(file.getInputStream(), file.getSize(), StorePath storePath = storageClient.uploadFile(file.getInputStream(), file.getSize(),
FilenameUtils.getExtension(file.getOriginalFilename()), null); FilenameUtils.getExtension(file.getOriginalFilename()), null);
return domain + "/" + storePath.getFullPath(); // 形如: http://47.99.175.191:8888/group1/M00/00/00/rBzzjWD-ec2ADLS9AAJiu1rRenk51.jpeg
return fastDfsConfig.getDomain() + "/" + storePath.getFullPath();
}
@Override
public String presignedUrl(String fileUrl) {
if (StringUtils.isBlank(fastDfsConfig.getTokenSecretKey())) {
throw new FileException(HttpStatus.ERROR + "", new String[] {"防盗链生成token的密钥为空请检查tokenSecretKey"});
}
String signKey = "?token=";
if (fileUrl.contains(signKey)) {
return fileUrl;
}
String tokenSecretKey = fastDfsConfig.getTokenSecretKey();
StorePath storePath = StorePath.parseFromUrl(fileUrl);
String keyPath = storePath.getPath();
//时间戳 单位为秒
int ts = (int) (System.currentTimeMillis() / 1000);
String token;
try {
token = ProtoCommon.getToken(keyPath, ts, tokenSecretKey);
} catch (Exception e) {
throw new FileException(HttpStatus.ERROR + "", new String[] {"FastDFS获取token异常"});
}
// 形如: http://47.99.175.191:8888/group1/M00/00/00/rBzzjWD-ec2ADLS9AAJiu1rRenk51.jpeg
return fastDfsConfig.getDomain() + "/" + storePath.getFullPath() + "?token=" + token + "&ts=" + ts;
} }
} }

@ -17,4 +17,21 @@ public interface ISysFileService
* @throws Exception * @throws Exception
*/ */
public String uploadFile(MultipartFile file) throws Exception; public String uploadFile(MultipartFile file) throws Exception;
/**
* 访url
* AWS Security Token Service (STS) (federation token)
*
* 1aliyun oss STS Security Token ServiceSTS https://help.aliyun.com/document_detail/28761.html?spm=a2c4g.11186623.6.880.22bd2fe5pL1d39
* 2minio resignedGetObject Security Token ServiceSTS; Presigned presignedGetObject
* http://docs.minio.org.cn/docs/master/minio-sts-quickstart-guide
* minio SDKS Java Client API http://docs.minio.org.cn/docs/master/java-client-api-reference
* 3qiniu (Bucket )==使====== https://developer.qiniu.com/kodo/1202/download-token
* https://developer.qiniu.com/kodo/5914/s3-compatible-sts
* 4 访 GetFederationToken 使 https://cloud.tencent.com/document/product/436/14048?from=10680
* 5fastdfs fastdfs https://www.cnblogs.com/xiaolinstudy/p/9341779.html
* @param fileUrl 访,
* @return url
*/
String presignedUrl(String fileUrl);
} }

@ -47,4 +47,9 @@ public class LocalSysFileServiceImpl implements ISysFileService
String url = domain + localFilePrefix + name; String url = domain + localFilePrefix + name;
return url; return url;
} }
@Override
public String presignedUrl(String fileUrl) {
return fileUrl;
}
} }

@ -1,5 +1,8 @@
package com.ruoyi.file.service; package com.ruoyi.file.service;
import io.minio.GetPresignedObjectUrlArgs;
import io.minio.errors.*;
import io.minio.http.Method;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
@ -8,10 +11,24 @@ import com.ruoyi.file.utils.FileUploadUtils;
import io.minio.MinioClient; import io.minio.MinioClient;
import io.minio.PutObjectArgs; import io.minio.PutObjectArgs;
import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.concurrent.TimeUnit;
/** /**
* Minio * Minio
* *
* @author ruoyi * @author ruoyi
* http://docs.minio.org.cn/docs/master/java-client-quickstart-guidec
* springboot使Minio8 https://springboot.io/t/topic/3109
* 1putObject
* 2removeObject
* ==================
* 3(Presigned)访urlstspresignedGetObject
* presignedGetObject(String bucketName, String objectName, Integer expires)
* http://docs.minio.org.cn/docs/master/java-client-api-reference#presignedGetObject
* MinIO STS http://docs.minio.org.cn/docs/master/minio-sts-quickstart-guide
*/ */
@Service @Service
public class MinioSysFileServiceImpl implements ISysFileService public class MinioSysFileServiceImpl implements ISysFileService
@ -42,4 +59,60 @@ public class MinioSysFileServiceImpl implements ISysFileService
client.putObject(args); client.putObject(args);
return minioConfig.getUrl() + "/" + minioConfig.getBucketName() + "/" + fileName; return minioConfig.getUrl() + "/" + minioConfig.getBucketName() + "/" + fileName;
} }
/**
* URL add
* [STS ]
* http://docs.minio.org.cn/docs/master/minio-sts-quickstart-guide
* minio SDKS Java Client API http://docs.minio.org.cn/docs/master/java-client-api-reference
* Presigned presignedGetObject
* https://xxxx.xxx.gov.cn/file/2021/08/06/cd9dfbaa-8563-423a-bc3d-d0b15e781931.pdf?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=D99KGE6ZTQXSATTJWU24%2F20210809%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20210809T075702Z&X-Amz-Expires=86400&X-Amz-SignedHeaders=host&X-Amz-Signature=198c76edc57998f4dde72124952b43f0066c762356e485dd44d21df9cc7dad78
*/
@Override
public String presignedUrl(String fileUrl) {
if (minioConfig.getExpiryDuration() == -1) {
return fileUrl;
}
String signKey = "?X-Amz-Algorithm=";
if (fileUrl.contains(signKey)) {
return fileUrl;
}
String objectName = this.getStorePath(fileUrl);
GetPresignedObjectUrlArgs args = GetPresignedObjectUrlArgs.builder().
bucket(minioConfig.getBucketName()).
method(Method.GET).
object(objectName).
expiry(minioConfig.getExpiryDuration(), TimeUnit.SECONDS).build();
String presignedObjectUrl = null;
try {
presignedObjectUrl = client.getPresignedObjectUrl(args);
String basePrivateUrl = minioConfig.getUrl() + "/" + minioConfig.getBucketName() + "/";
presignedObjectUrl = presignedObjectUrl.replace(basePrivateUrl, "");
} catch (ErrorResponseException | InsufficientDataException | InternalException | InvalidKeyException | InvalidResponseException | IOException | NoSuchAlgorithmException | XmlParserException | ServerException e) {
e.printStackTrace();
presignedObjectUrl = fileUrl;
}
return minioConfig.getDomain() + "/" + minioConfig.getBucketName() + "/" + presignedObjectUrl;
}
/**
* urlkey
*
* @param filePath https://yq666.bj.gov.cn/appt-file/dev/default/2021/07/18/f4243eb2-06a1-4304-bdfc-e2964b8721bb.jpeg
* @return dev/default/2021/07/18/f4243eb2-06a1-4304-bdfc-e2964b8721bb.jpeg
*/
private String getStorePath(String filePath) {
String oldPath = filePath;
// 处理方式1
String publicPath3 = minioConfig.getDomain() + "/" + minioConfig.getBucketName() + "/";
filePath = filePath.replace(publicPath3, "");
if (oldPath.equals(filePath)) {
// 处理方式2
String publicPath4 = minioConfig.getUrl() + "/" + minioConfig.getBucketName() + "/";
filePath = filePath.replace(publicPath4, "");
}
return filePath;
}
} }

Loading…
Cancel
Save