feat(file-upload): 集成新疆油田OSS文件上传功能并优化设备管理
- 集成XjOssUtil实现新疆油田平台OSS文件上传下载功能 - 添加XjOssConfig配置类从Nacos读取OSS相关配置信息 - 实现OAuth2 client_credentials鉴权机制并缓存token到Redis - 支持.tar文件强制使用MyUpload上传,其他文件根据oss_flag配置选择上传方式 - 在WatchDeviceController中禁用移动端解绑手表功能并添加删除设备时的绑定检查 - 添加/sys/upload/down/**和/sys/upload/show/**匿名访问权限支持文件下载预览 - 实现文件下载预览接口支持IOSP平台文件服务并添加安全校验机制 - 在CareUserDetailsParam中添加年份字段默认为当前年份
This commit is contained in:
+100
@@ -1,10 +1,12 @@
|
||||
package org.jeecg.modules.system.controller;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.jeecg.common.api.vo.Result;
|
||||
import org.jeecg.common.util.MyUploadUtil;
|
||||
import org.jeecg.common.util.XjOssUtil;
|
||||
import org.jeecg.common.util.oConvertUtils;
|
||||
import org.jeecg.modules.oss.service.IOssFileService;
|
||||
import org.jeecg.modules.system.bean.request.UploadFilsDTO;
|
||||
@@ -15,6 +17,11 @@ import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
@@ -89,6 +96,9 @@ public class SysUploadController {
|
||||
} catch (Exception e) {
|
||||
return Result.error(e.getMessage());
|
||||
}
|
||||
if (StrUtil.isBlank(fileUrl)){
|
||||
return Result.error("上传失败,请检查配置信息是否正确!");
|
||||
}
|
||||
Map map = new HashMap();
|
||||
map.put("url", fileUrl);
|
||||
return Result.OK(map);
|
||||
@@ -115,4 +125,94 @@ public class SysUploadController {
|
||||
return Result.OK(urlList);
|
||||
}
|
||||
|
||||
@Operation(summary = "IOSP文件下载", description = "根据 fileKey 下载文件,如 sys/upload/down/808fec...@aa.png")
|
||||
@RequestMapping(value = "/down/**", method = RequestMethod.GET)
|
||||
public void fileDown(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
String fileKey = extractPathParam(request, "/down/");
|
||||
if (isIllegalFileKey(fileKey)) {
|
||||
response.sendError(HttpServletResponse.SC_BAD_REQUEST, "fileKey 格式非法");
|
||||
return;
|
||||
}
|
||||
try (InputStream in = XjOssUtil.down(fileKey)) {
|
||||
if (in == null) {
|
||||
response.sendError(HttpServletResponse.SC_NOT_FOUND, "文件不存在或下载失败");
|
||||
return;
|
||||
}
|
||||
// 从 fileKey 中取文件名(@ 后面的部分)
|
||||
String fileName = fileKey.contains("@") ? fileKey.substring(fileKey.indexOf("@") + 1) : fileKey;
|
||||
response.setContentType("application/octet-stream");
|
||||
response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
|
||||
try (OutputStream out = response.getOutputStream()) {
|
||||
byte[] buf = new byte[4096];
|
||||
int len;
|
||||
while ((len = in.read(buf)) != -1) {
|
||||
out.write(buf, 0, len);
|
||||
}
|
||||
out.flush();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("[SysUploadController] IOSP文件下载失败, fileKey={}", fileKey, e);
|
||||
}
|
||||
}
|
||||
|
||||
@Operation(summary = "IOSP文件预览", description = "根据 fileKey 获取预览地址,如 sys/upload/show/808fec...@aa.png")
|
||||
@RequestMapping(value = "/show/**", method = RequestMethod.GET)
|
||||
public void fileShow(HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
String fileKey = extractPathParam(request, "/show/");
|
||||
if (isIllegalFileKey(fileKey)) {
|
||||
response.sendError(HttpServletResponse.SC_BAD_REQUEST, "fileKey 格式非法");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
String url = XjOssUtil.show(fileKey);
|
||||
if (url == null) {
|
||||
response.sendError(HttpServletResponse.SC_NOT_FOUND, "文件不存在或获取预览地址失败");
|
||||
return;
|
||||
}
|
||||
// 重定向到真实预览地址
|
||||
response.sendRedirect(url);
|
||||
} catch (Exception e) {
|
||||
log.error("[SysUploadController] IOSP文件预览失败, fileKey={}", fileKey, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从请求路径中提取 fileKey
|
||||
* <p>截取 marker 之后的路径部分,并做 URL 解码</p>
|
||||
*/
|
||||
private String extractPathParam(HttpServletRequest request, String marker) {
|
||||
String uri = request.getRequestURI();
|
||||
int idx = uri.indexOf(marker);
|
||||
if (idx < 0) {
|
||||
return "";
|
||||
}
|
||||
String raw = uri.substring(idx + marker.length());
|
||||
try {
|
||||
return java.net.URLDecoder.decode(raw, "UTF-8");
|
||||
} catch (Exception e) {
|
||||
return raw;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验 fileKey 是否存在路径穿透风险
|
||||
* <p>
|
||||
* 拦截 ../、..\ 及其 URL 编码变体(%2e%2e%2f 等),防止跳出授权目录
|
||||
* </p>
|
||||
*/
|
||||
private boolean isIllegalFileKey(String fileKey) {
|
||||
if (oConvertUtils.isEmpty(fileKey)) {
|
||||
return true;
|
||||
}
|
||||
// 先做 URL 解码,防止编码绕过
|
||||
String decoded;
|
||||
try {
|
||||
decoded = java.net.URLDecoder.decode(fileKey, "UTF-8");
|
||||
} catch (Exception e) {
|
||||
return true;
|
||||
}
|
||||
return decoded.contains("../") || decoded.contains("..\\")
|
||||
|| decoded.startsWith("..") || decoded.contains("/..");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user