This commit is contained in:
777
2025-11-18 12:53:40 +08:00
parent 7a2965c2f8
commit 53e3adb498
4 changed files with 177 additions and 4 deletions

View File

@@ -1,22 +1,26 @@
package com.ruoyi.web.controller.cai.app; package com.ruoyi.web.controller.cai.app;
import cn.dev33.satoken.annotation.SaIgnore;
import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.ObjectUtil;
import com.ruoyi.cai.dto.FileResp; import com.ruoyi.cai.dto.FileResp;
import com.ruoyi.cai.dto.app.UploadFileResp;
import com.ruoyi.cai.enums.SystemConfigEnum;
import com.ruoyi.cai.manager.SystemConfigManager;
import com.ruoyi.cai.util.OfflineTokenManager;
import com.ruoyi.common.annotation.Log; import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.domain.R; import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.enums.BusinessType; import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.helper.LoginHelper;
import com.ruoyi.system.domain.vo.SysOssVo; import com.ruoyi.system.domain.vo.SysOssVo;
import com.ruoyi.system.service.ISysOssService; import com.ruoyi.system.service.ISysOssService;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
import jdk.nashorn.internal.ir.annotations.Ignore;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
@RestController @RestController
@@ -27,6 +31,8 @@ public class FileController {
@Autowired @Autowired
private ISysOssService iSysOssService; private ISysOssService iSysOssService;
@Autowired
private SystemConfigManager systemConfigManager;
@Log(title = "OSS对象存储", businessType = BusinessType.INSERT) @Log(title = "OSS对象存储", businessType = BusinessType.INSERT)
@PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) @PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
@@ -49,6 +55,43 @@ public class FileController {
return R.ok(resp); return R.ok(resp);
} }
@GetMapping("/uploadFileSign")
public R<UploadFileResp> uploadFileSign() throws Exception {
String uploadFileDomain = systemConfigManager.getSystemConfig(SystemConfigEnum.UPLOAD_FILE_DOMAIN);
String s = OfflineTokenManager.generateToken(LoginHelper.getUserId() + "");
UploadFileResp resp = new UploadFileResp();
resp.setDomain(uploadFileDomain);
resp.setToken(s);
resp.setHttpUrl(uploadFileDomain+"/api/file/uploadImageV2");
return R.ok(resp);
}
@Log(title = "OSS对象存储", businessType = BusinessType.INSERT)
@PostMapping(value = "/uploadImageV2", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
@Operation(summary = "上传图片类型的文件",
parameters = {
@Parameter(name = "file", description = "文件", required = true),
@Parameter(name = "type", description = "业务类型dynamic=动态图片user=用户相册头像im=聊天common=其他", required = false)
})
@SaIgnore
public R<FileResp> uploadImageV1(@RequestPart("file") MultipartFile file,
String type, String token) {
boolean success = OfflineTokenManager.validateToken(token);
if(!success){
return R.fail("上传失败token校验失败");
}
log.error("上传文件图片类型 type={}",type);
if (ObjectUtil.isNull(file)) {
return R.fail("上传文件不能为空");
}
SysOssVo oss = iSysOssService.upload(file);
FileResp resp = new FileResp();
resp.setUrl(oss.getUrl());
resp.setPath(oss.getFileName());
resp.setOriginalName(oss.getOriginalName());
return R.ok(resp);
}
@Log(title = "OSS对象存储", businessType = BusinessType.INSERT) @Log(title = "OSS对象存储", businessType = BusinessType.INSERT)
@PostMapping(value = "/uploadImage", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) @PostMapping(value = "/uploadImage", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)

View File

@@ -0,0 +1,12 @@
package com.ruoyi.cai.dto.app;
import lombok.Data;
import java.io.Serializable;
@Data
public class UploadFileResp implements Serializable {
private String token;
private String domain;
private String httpUrl;
}

View File

@@ -143,6 +143,7 @@ public enum SystemConfigEnum {
V12_XIAOCHENGXU_ORG_ID("gh_62790d4f9c57", "V12德商小程序原始id",SystemConfigGroupEnum.SYSTEM), V12_XIAOCHENGXU_ORG_ID("gh_62790d4f9c57", "V12德商小程序原始id",SystemConfigGroupEnum.SYSTEM),
V12_XIAOCHENGXU_PATH("pages/zf/index?", "V12德商小程序页面路径",SystemConfigGroupEnum.SYSTEM), V12_XIAOCHENGXU_PATH("pages/zf/index?", "V12德商小程序页面路径",SystemConfigGroupEnum.SYSTEM),
V12_WX_APP_ID("wxae39c7eed3221d26", "微信开放平台ID",SystemConfigGroupEnum.SYSTEM), V12_WX_APP_ID("wxae39c7eed3221d26", "微信开放平台ID",SystemConfigGroupEnum.SYSTEM),
UPLOAD_FILE_DOMAIN("http://127.0.0.1:8080", "文件上传域名服务器",SystemConfigGroupEnum.SYSTEM),
// 七牛云 ?imageView2/2/w/120/h/120 // 七牛云 ?imageView2/2/w/120/h/120
// 腾讯云 ?thumbnail=120y120&imageView // 腾讯云 ?thumbnail=120y120&imageView
IM_ICON_SUFFIX("?thumbnail=120y120&imageView", "im头像后缀",SystemConfigGroupEnum.SYSTEM), IM_ICON_SUFFIX("?thumbnail=120y120&imageView", "im头像后缀",SystemConfigGroupEnum.SYSTEM),

View File

@@ -0,0 +1,117 @@
package com.ruoyi.cai.util;
import com.esotericsoftware.minlog.Log;
import lombok.extern.slf4j.Slf4j;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
@Slf4j
public class OfflineTokenManager {
// 加密密钥离线系统需保持一致建议16位字符
private static final String SECRET_KEY = "7ZxQ9kL2pF4sT5mR"; // 16位密钥
// 有效期2小时单位毫秒
private static final long VALID_DURATION = 3 * 60 * 60 * 1000;
/**
* 生成包含时间戳的token
* @param userId 用户标识(可替换为其他业务唯一标识)
* @return 加密后的token
* @throws Exception 加密异常
*/
public static String generateToken(String userId) throws Exception {
// 获取当前时间戳(毫秒)
long timestamp = System.currentTimeMillis();
// 拼接用户ID和时间戳格式userId:timestamp
String content = userId + ":" + timestamp;
// 加密并返回Base64编码的token
return encrypt(content);
}
/**
* 校验token有效性
* @param token 待校验的token
* @return 校验结果true-有效false-无效
*/
public static boolean validateToken(String token) {
try {
// 解密token
String decryptedContent = decrypt(token);
// 解析内容格式userId:timestamp
String[] parts = decryptedContent.split(":", 2);
if (parts.length != 2) {
return false; // 格式错误
}
// 提取时间戳并校验
long tokenTimestamp = Long.parseLong(parts[1]);
long currentTime = System.currentTimeMillis();
// 检查是否在有效期内(允许一定时间误差,避免时钟细微差异)
// return currentTime - tokenTimestamp <= VALID_DURATION && currentTime >= tokenTimestamp;
return currentTime - tokenTimestamp <= VALID_DURATION;
} catch (Exception e) {
// 解密失败或格式错误均视为无效
log.error("解析失败问题",e);
return false;
}
}
/**
* 从有效token中提取用户ID
* @param token 已通过校验的token
* @return 用户ID
* @throws Exception 解析异常
*/
public static String getUserIdFromToken(String token) throws Exception {
String decryptedContent = decrypt(token);
String[] parts = decryptedContent.split(":", 2);
return parts[0];
}
// AES加密
private static String encrypt(String content) throws Exception {
SecretKeySpec keySpec = new SecretKeySpec(SECRET_KEY.getBytes(StandardCharsets.UTF_8), "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, keySpec);
byte[] encrypted = cipher.doFinal(content.getBytes(StandardCharsets.UTF_8));
return Base64.getEncoder().encodeToString(encrypted);
}
// AES解密
private static String decrypt(String token) throws Exception {
SecretKeySpec keySpec = new SecretKeySpec(SECRET_KEY.getBytes(StandardCharsets.UTF_8), "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, keySpec);
byte[] decrypted = cipher.doFinal(Base64.getDecoder().decode(token));
return new String(decrypted, StandardCharsets.UTF_8);
}
// 测试示例
public static void main(String[] args) throws Exception {
// 生成token
// String userId = "system_user_123";
// String token = generateToken(userId);
// System.out.println("生成的token: " + token);
// 校验token立即校验
boolean isValid = validateToken("Kor2eFRylHp+3tP6xevf7fCeH0TvBJsXFPuxHWtFkss=");
System.out.println("token是否有效: " + isValid);
// 提取用户ID
// if (isValid) {
// String extractedUserId = getUserIdFromToken(token);
// System.out.println("从token中提取的用户ID: " + extractedUserId);
// }
// 模拟2小时后校验实际场景中无需此代码仅作测试
// Thread.sleep(VALID_DURATION + 1000);
// boolean isExpired = validateToken(token);
// System.out.println("2小时后token是否有效: " + isExpired);
}
}