集成onlyoffice预览和在线编辑
This commit is contained in:
parent
a04e5a7c4a
commit
818b4e7566
|
|
@ -1,44 +1,36 @@
|
|||
package com.bonus.web.controller.common;
|
||||
|
||||
import com.bonus.common.core.domain.AjaxResult;
|
||||
import com.bonus.common.domain.file.vo.OnlyOfficeCallback;
|
||||
import com.bonus.file.service.OnlyOfficeService;
|
||||
import com.bonus.common.domain.file.vo.Callback;
|
||||
import com.bonus.web.service.common.DocumentService;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/documents")
|
||||
@CrossOrigin(origins = "*")
|
||||
public class DocumentController {
|
||||
|
||||
@Resource(name = "OnlyOfficeService")
|
||||
private OnlyOfficeService onlyOfficeService;
|
||||
|
||||
@Resource
|
||||
private DocumentService documentService;
|
||||
|
||||
@ApiOperation(value = "获取onlyoffice配置", notes = "获取onlyoffice配置")
|
||||
@GetMapping("/config")
|
||||
public AjaxResult getEditorConfig(
|
||||
@RequestParam String fileId,
|
||||
@RequestParam String fileName,
|
||||
@RequestParam String type,
|
||||
@RequestParam(defaultValue = "view") String mode) {
|
||||
try {
|
||||
Map<String, Object> config = onlyOfficeService.getConfigWithToken(fileId, fileName, mode);
|
||||
return AjaxResult.success(config);
|
||||
} catch (Exception e) {
|
||||
return AjaxResult.error();
|
||||
}
|
||||
return documentService.getEditorConfig(fileId,type,mode);
|
||||
}
|
||||
|
||||
@ApiOperation(value = "onlyoffice文档编辑回调", notes = "onlyoffice文档编辑回调")
|
||||
@PostMapping("/callback")
|
||||
public AjaxResult handleCallback(@RequestBody OnlyOfficeCallback callback) {
|
||||
try {
|
||||
onlyOfficeService.handleCallback(callback);
|
||||
return AjaxResult.success();
|
||||
} catch (Exception e) {
|
||||
return AjaxResult.error();
|
||||
}
|
||||
public String handleCallback(@RequestBody Callback callback, @RequestParam String fileUrl, HttpServletRequest request, HttpServletResponse response) {
|
||||
return documentService.handleCallback(callback,fileUrl,request,response);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
package com.bonus.web.service.common;
|
||||
|
||||
import com.bonus.common.core.domain.AjaxResult;
|
||||
import com.bonus.common.domain.file.po.ResourceFilePo;
|
||||
import com.bonus.common.domain.file.vo.Callback;
|
||||
import com.bonus.common.domain.file.vo.ResourceFileVo;
|
||||
import com.bonus.file.service.OnlyOfficeService2;
|
||||
import com.bonus.file.service.SourceFileService;
|
||||
import com.bonus.framework.web.service.TokenService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @className:DocumentService
|
||||
* @author:cwchen
|
||||
* @date:2025-11-06-15:09
|
||||
* @version:1.0
|
||||
* @description:onlyoffice文档业务层
|
||||
*/
|
||||
@Service(value = "DocumentService")
|
||||
@Slf4j
|
||||
public class DocumentService {
|
||||
|
||||
@Resource(name = "OnlyOfficeService2")
|
||||
private OnlyOfficeService2 onlyOfficeService2;
|
||||
|
||||
@Resource
|
||||
private TokenService tokenService;
|
||||
|
||||
@Resource(name = "SourceFileService")
|
||||
private SourceFileService sourceFileService;
|
||||
|
||||
|
||||
/**
|
||||
* 获取onlyoffice配置
|
||||
*
|
||||
* @param fileId
|
||||
* @param fileName
|
||||
* @param type
|
||||
* @param mode
|
||||
* @return AjaxResult
|
||||
* @author cwchen
|
||||
* @date 2025/11/6 15:15
|
||||
*/
|
||||
public AjaxResult getEditorConfig(String fileId, String type, String mode) {
|
||||
try {
|
||||
ResourceFilePo resourceFilePo = new ResourceFilePo();
|
||||
resourceFilePo.setSourceId(Long.parseLong(fileId));
|
||||
ResourceFileVo fileVo = sourceFileService.getFileById(resourceFilePo);
|
||||
if(Objects.isNull(fileVo)) {
|
||||
return AjaxResult.error("文件不存在");
|
||||
}
|
||||
// config = onlyOfficeService.getConfigWithToken(fileId, fileName, type, mode);
|
||||
return onlyOfficeService2.getOnlyOfficeConfig(fileVo,type,mode);
|
||||
} catch (Exception e) {
|
||||
log.error(e.toString(),e);
|
||||
return AjaxResult.error();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* onlyoffice文档编辑回调
|
||||
*
|
||||
* @param callback
|
||||
* @return AjaxResult
|
||||
* @author cwchen
|
||||
* @date 2025/11/6 15:16
|
||||
*/
|
||||
public String handleCallback(Callback callback, String fileUrl, HttpServletRequest request, HttpServletResponse response) {
|
||||
try {
|
||||
log.info("收到 OnlyOffice 回调, 状态: {}, Key: {}", callback.getStatus(), callback.getKey());
|
||||
log.info("callback:{}",callback);
|
||||
// onlyOfficeService.handleCallback(callback,response);
|
||||
return onlyOfficeService2.handleCallback(callback,fileUrl,request,response);
|
||||
} catch (Exception e) {
|
||||
log.error(e.toString(),e);
|
||||
return "{\"error\":\"1\"}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -203,6 +203,25 @@
|
|||
<artifactId>jjwt</artifactId>
|
||||
<version>${io.jsonwebtoken.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!--onlyoffice集成的sdk-->
|
||||
<!--<dependency>
|
||||
<groupId>com.onlyoffice</groupId>
|
||||
<artifactId>docs-integration-sdk</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</dependency>-->
|
||||
|
||||
<dependency>
|
||||
<groupId>com.auth0</groupId>
|
||||
<artifactId>java-jwt</artifactId>
|
||||
<version>3.19.2</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-all</artifactId>
|
||||
<version>5.8.11</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
package com.bonus.common.domain.file.vo;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @className:Callback
|
||||
* @author:cwchen
|
||||
* @date:2025-11-08-1:54
|
||||
* @version:1.0
|
||||
* @description:onlyoffice回调参数
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Accessors(chain = true)
|
||||
public class Callback {
|
||||
|
||||
private String key;
|
||||
private int status;
|
||||
private String url;
|
||||
@JsonProperty("changesurl")
|
||||
private String changesUrl;
|
||||
private History history;
|
||||
private List<String> users;
|
||||
private List<Map<String, Object>> actions;
|
||||
@JsonProperty("lastsave")
|
||||
private String lastSave;
|
||||
@JsonProperty("forcesavetype")
|
||||
private int forceSaveType;
|
||||
private String token;
|
||||
private String filetype;
|
||||
|
||||
// History 内部类
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Accessors(chain = true)
|
||||
public static class History {
|
||||
private String serverVersion;
|
||||
private List<Change> changes;
|
||||
|
||||
// Change 内部类
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Accessors(chain = true)
|
||||
public static class Change {
|
||||
private String created;
|
||||
private User user;
|
||||
|
||||
// User 内部类
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Accessors(chain = true)
|
||||
public static class User {
|
||||
private String id;
|
||||
private String name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -14,12 +14,19 @@ import java.util.Map;
|
|||
*/
|
||||
@Data
|
||||
public class OnlyOfficeCallback {
|
||||
/**
|
||||
* 0 - 为命令服务执行强制保存请求,
|
||||
* 1 - 每次保存完成时都会执行强制保存请求(例如单击 保存 按钮),这仅在 forcesave 选项设置为 true时可用,
|
||||
* 2 - 强制保存请求由计时器按服务器配置中的设置执行,
|
||||
* 3 - 每次提交表单时都会执行强制保存请求 Complete & Submit 按钮被点击 )。
|
||||
*/
|
||||
private Integer forcesavetype;
|
||||
private Integer status;
|
||||
private String key;
|
||||
private String url;
|
||||
private List<Map<String, Object>> actions;
|
||||
private String lastsave;
|
||||
private Boolean users;
|
||||
// private Boolean users;
|
||||
private Boolean notmodified;
|
||||
|
||||
// 状态常量
|
||||
|
|
|
|||
|
|
@ -0,0 +1,127 @@
|
|||
package com.bonus.common.domain.file.vo;
|
||||
|
||||
import com.bonus.common.utils.StringUtils;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* onlyOffice配置
|
||||
* 这里的配置会从 application.yml或application.properties 中读取
|
||||
*/
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Component(value = "OnlyOfficeConfig")
|
||||
@ConfigurationProperties(prefix = "only-office")
|
||||
public class OnlyOfficeConfig implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private String secret;
|
||||
private Config config;
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public static class Config implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private Document document;
|
||||
private EditorConfig editorConfig;
|
||||
private String type;
|
||||
private String token;
|
||||
private String documentType;
|
||||
private String height = "100%";
|
||||
private String width = "100%";
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public static class Document implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private String title;
|
||||
private String fileType;
|
||||
private String key;
|
||||
private String url;
|
||||
private Permissions permissions;
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public static class Permissions implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private Boolean edit;
|
||||
private Boolean print;
|
||||
private Boolean download;
|
||||
private Boolean fillForms;
|
||||
private Boolean review;
|
||||
}
|
||||
}
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public static class EditorConfig implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private String callbackUrl;
|
||||
private String lang;
|
||||
private CoEditing coEditing;
|
||||
private Customization customization;
|
||||
private String region;
|
||||
private User user;
|
||||
public User getUser(){
|
||||
return StringUtils.isNull(user)?new User():user;
|
||||
}
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public static class CoEditing implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private String mode;
|
||||
private Boolean change;
|
||||
}
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public static class Customization implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private Boolean forcesave;
|
||||
private Boolean autosave;
|
||||
private Boolean comments;
|
||||
private Boolean compactHeader;
|
||||
private Boolean compactToolbar;
|
||||
private Boolean compatibleFeatures;
|
||||
private Features features;
|
||||
|
||||
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public static class Features implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private Spellcheck spellcheck;
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public static class Spellcheck implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private Boolean mode;
|
||||
private Boolean change;
|
||||
}
|
||||
}
|
||||
}
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public static class User implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private String id;
|
||||
private String name;
|
||||
private String image;
|
||||
private String group;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
package com.bonus.common.enums;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
import com.fasterxml.jackson.annotation.JsonValue;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @className:OnlyOfficeStatus枚举
|
||||
* @author:cwchen
|
||||
* @date:2025-11-08-2:06
|
||||
* @version:1.0
|
||||
* @description:
|
||||
*/
|
||||
public enum OnlyOfficeStatus {
|
||||
EDITING(1),
|
||||
SAVE(2),
|
||||
SAVE_CORRUPTED(3),
|
||||
CLOSED(4),
|
||||
FORCESAVE(6),
|
||||
FORCESAVE_CORRUPTED(7);
|
||||
|
||||
private final int id;
|
||||
private static final Map<Integer, OnlyOfficeStatus> BY_ID = new HashMap<>();
|
||||
|
||||
static {
|
||||
for (OnlyOfficeStatus status : values()) {
|
||||
BY_ID.put(status.id, status);
|
||||
}
|
||||
}
|
||||
|
||||
OnlyOfficeStatus(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@JsonValue
|
||||
public int getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
@JsonCreator
|
||||
public static OnlyOfficeStatus valueOfId(Integer code) {
|
||||
if (code == null) {
|
||||
return null;
|
||||
}
|
||||
return BY_ID.get(code);
|
||||
}
|
||||
|
||||
// 添加一些实用方法
|
||||
public static OnlyOfficeStatus fromId(int id) {
|
||||
return BY_ID.get(id);
|
||||
}
|
||||
|
||||
public boolean isSaveStatus() {
|
||||
return this == SAVE || this == FORCESAVE;
|
||||
}
|
||||
|
||||
public boolean isErrorStatus() {
|
||||
return this == SAVE_CORRUPTED || this == FORCESAVE_CORRUPTED;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
package com.bonus.common.utils;
|
||||
|
||||
/**
|
||||
* @className:DocumentTypeUtils
|
||||
* @author:cwchen
|
||||
* @date:2025-11-08-3:24
|
||||
* @version:1.0
|
||||
* @description:
|
||||
*/
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class DocumentTypeUtils {
|
||||
|
||||
private static final Map<String, String> EXTENSION_TO_TYPE = new HashMap<>();
|
||||
|
||||
static {
|
||||
EXTENSION_TO_TYPE.put("docx", "word");
|
||||
EXTENSION_TO_TYPE.put("doc", "word");
|
||||
EXTENSION_TO_TYPE.put("pdf", "word");
|
||||
EXTENSION_TO_TYPE.put("pptx", "slide");
|
||||
EXTENSION_TO_TYPE.put("ppt", "slide");
|
||||
EXTENSION_TO_TYPE.put("xlsx", "cell");
|
||||
EXTENSION_TO_TYPE.put("xls", "cell");
|
||||
// 可以继续添加其他格式
|
||||
}
|
||||
|
||||
public static String getDocumentType(String fileUrl) {
|
||||
if (fileUrl == null || fileUrl.isEmpty()) {
|
||||
return "word";
|
||||
}
|
||||
|
||||
String extension = getFileExtension(fileUrl);
|
||||
return EXTENSION_TO_TYPE.getOrDefault(extension, "word");
|
||||
}
|
||||
|
||||
public static String getFileExtension(String fileUrl) {
|
||||
if (fileUrl == null || fileUrl.isEmpty()) {
|
||||
return "";
|
||||
}
|
||||
|
||||
String normalizedUrl = fileUrl.toLowerCase().trim();
|
||||
int lastDotIndex = normalizedUrl.lastIndexOf(".");
|
||||
int lastSlashIndex = Math.max(normalizedUrl.lastIndexOf("/"), normalizedUrl.lastIndexOf("\\"));
|
||||
|
||||
// 确保点号在最后一个斜杠之后
|
||||
if (lastDotIndex > lastSlashIndex && lastDotIndex < normalizedUrl.length() - 1) {
|
||||
return normalizedUrl.substring(lastDotIndex + 1);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
// 额外工具方法
|
||||
public static boolean isSupportedDocument(String fileUrl) {
|
||||
String extension = getFileExtension(fileUrl);
|
||||
return EXTENSION_TO_TYPE.containsKey(extension);
|
||||
}
|
||||
|
||||
public static String getFileName(String fileUrl) {
|
||||
if (fileUrl == null || fileUrl.isEmpty()) {
|
||||
return "";
|
||||
}
|
||||
String normalizedUrl = fileUrl.replace('\\', '/');
|
||||
int lastSlashIndex = normalizedUrl.lastIndexOf("/");
|
||||
return lastSlashIndex == -1 ? normalizedUrl : normalizedUrl.substring(lastSlashIndex + 1);
|
||||
}
|
||||
|
||||
// 添加更多实用方法
|
||||
public static boolean isWordDocument(String fileUrl) {
|
||||
return "word".equals(getDocumentType(fileUrl));
|
||||
}
|
||||
|
||||
public static boolean isSlideDocument(String fileUrl) {
|
||||
return "slide".equals(getDocumentType(fileUrl));
|
||||
}
|
||||
|
||||
public static boolean isCellDocument(String fileUrl) {
|
||||
return "cell".equals(getDocumentType(fileUrl));
|
||||
}
|
||||
}
|
||||
|
|
@ -11,7 +11,7 @@ import org.springframework.stereotype.Component;
|
|||
* @version: 1.0
|
||||
* @description: OnlyOffice 配置
|
||||
*/
|
||||
@Component(value = "OnlyOfficeConfig")
|
||||
/*@Component(value = "OnlyOfficeConfig")
|
||||
@Data
|
||||
public class OnlyOfficeConfig {
|
||||
|
||||
|
|
@ -27,4 +27,4 @@ public class OnlyOfficeConfig {
|
|||
@Value("${server.port}")
|
||||
private String serverPort;
|
||||
|
||||
}
|
||||
}*/
|
||||
|
|
|
|||
|
|
@ -0,0 +1,165 @@
|
|||
package com.bonus.file.service;
|
||||
|
||||
import com.bonus.common.domain.file.vo.Callback;
|
||||
import com.bonus.common.enums.OnlyOfficeStatus;
|
||||
import com.bonus.common.utils.FileUtil;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.json.JSONObject;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/**
|
||||
* @className:CallBackService
|
||||
* @author:cwchen
|
||||
* @date:2025-11-08-0:28
|
||||
* @version:1.0
|
||||
* @description:onlyoffice回调业务层
|
||||
*/
|
||||
@Service(value = "CallBackService")
|
||||
public class CallBackService {
|
||||
|
||||
@Value("${only-office.jwt-enabled}")
|
||||
public Boolean jwtEnabled;
|
||||
|
||||
@Value("${only-office.secret}")
|
||||
public String secret;
|
||||
|
||||
@Resource(name = "FileUploadService")
|
||||
private FileUploadService fileUploadService;
|
||||
|
||||
@Resource(name = "OnlyOfficeJwtService")
|
||||
private OnlyOfficeJwtService onlyOfficeJwtService;
|
||||
|
||||
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
public Callback verifyCallback(Callback callback, String authorization) throws JsonProcessingException {
|
||||
// 判断是否开启jwt校验
|
||||
if (!Boolean.TRUE.equals(jwtEnabled)) {
|
||||
// 未开启jwt校验
|
||||
return callback;
|
||||
}
|
||||
String token = callback.getToken();
|
||||
boolean fromHeader = false;
|
||||
if (StringUtils.isEmpty(token) && StringUtils.isNotEmpty(authorization)) {
|
||||
token = authorization.replace("Bearer ", "");
|
||||
fromHeader = true;
|
||||
}
|
||||
if (StringUtils.isEmpty(token)) {
|
||||
throw new RuntimeException("token不存在");
|
||||
}
|
||||
String payload = onlyOfficeJwtService.verify(token);
|
||||
if (fromHeader) {
|
||||
JSONObject data = new JSONObject(payload);
|
||||
JSONObject jsonObject = data.getJSONObject("payload");
|
||||
return objectMapper.readValue(jsonObject.toString(), Callback.class);
|
||||
} else {
|
||||
return objectMapper.readValue(payload, Callback.class);
|
||||
}
|
||||
}
|
||||
|
||||
public void processCallback(Callback callback, String fileUrl) throws Exception {
|
||||
OnlyOfficeStatus status = OnlyOfficeStatus.fromId(callback.getStatus());
|
||||
switch (status) {
|
||||
case EDITING:
|
||||
this.handlerEditing(callback, fileUrl);
|
||||
break;
|
||||
case SAVE:
|
||||
this.handlerSave(callback, fileUrl);
|
||||
break;
|
||||
case SAVE_CORRUPTED:
|
||||
this.handlerSaveCorrupted(callback, fileUrl);
|
||||
break;
|
||||
case CLOSED:
|
||||
this.handlerClosed(callback, fileUrl);
|
||||
break;
|
||||
case FORCESAVE:
|
||||
this.handlerForcesave(callback, fileUrl);
|
||||
break;
|
||||
case FORCESAVE_CORRUPTED:
|
||||
this.handlerForcesaveCurrupted(callback, fileUrl);
|
||||
break;
|
||||
default:
|
||||
throw new RuntimeException("Callback has no status");
|
||||
}
|
||||
}
|
||||
|
||||
public void handlerEditing(Callback callback, String fileUrl) throws Exception {
|
||||
// 获取用户id
|
||||
List<String> users = callback.getUsers();
|
||||
}
|
||||
|
||||
public void handlerSave(Callback callback, String fileUrl) throws Exception {
|
||||
// onlyoffice文档保存
|
||||
String downloadUrl = callback.getUrl();
|
||||
if (StringUtils.isEmpty(downloadUrl)) {
|
||||
return;
|
||||
}
|
||||
// 获取文件字节数组
|
||||
byte[] fileContent = downloadFileFromUrl(downloadUrl);
|
||||
// 保存到 MinIO
|
||||
saveToMinio(fileUrl, fileContent);
|
||||
}
|
||||
|
||||
public void handlerSaveCorrupted(Callback callback, String fileUrl) throws Exception {
|
||||
handlerSave(callback, fileUrl);
|
||||
}
|
||||
|
||||
public void handlerClosed(Callback callback, String fileUrl) throws Exception {
|
||||
// 无需处理
|
||||
}
|
||||
|
||||
public void handlerForcesave(Callback callback, String fileUrl) throws Exception {
|
||||
handlerSave(callback, fileUrl);
|
||||
}
|
||||
|
||||
public void handlerForcesaveCurrupted(Callback callback, String fileUrl) throws Exception {
|
||||
handlerSave(callback, fileUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载最新文件
|
||||
* @param url
|
||||
* @return byte
|
||||
* @author cwchen
|
||||
* @date 2025/11/8 2:49
|
||||
*/
|
||||
private byte[] downloadFileFromUrl(String url) throws Exception {
|
||||
RestTemplate restTemplate = new RestTemplate();
|
||||
ResponseEntity<byte[]> response = restTemplate.getForEntity(url, byte[].class);
|
||||
return response.getBody();
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存最新文件到minio
|
||||
* @param filePath
|
||||
* @param content
|
||||
* @return void
|
||||
* @author cwchen
|
||||
* @date 2025/11/8 2:48
|
||||
*/
|
||||
private void saveToMinio(String filePath, byte[] content) throws Exception {
|
||||
String fileName = filePath.substring(filePath.lastIndexOf("/") + 1);
|
||||
ByteArrayInputStream inputStream = new ByteArrayInputStream(content);
|
||||
MultipartFile multipartFile = FileUtil.createMultipartFile(fileName, fileName,
|
||||
"application/octet-stream", inputStream);
|
||||
long size = multipartFile.getSize();
|
||||
long fiveMB = 5 * 1024 * 1024; // 5MB in bytes
|
||||
if (size > fiveMB) {
|
||||
// 处理大文件逻辑
|
||||
fileUploadService.uploadLargeFile(multipartFile, fileName);
|
||||
} else {
|
||||
// 处理正常文件逻辑
|
||||
fileUploadService.uploadFile(multipartFile, filePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,10 +1,15 @@
|
|||
package com.bonus.file.service;
|
||||
|
||||
import com.auth0.jwt.JWT;
|
||||
import com.auth0.jwt.algorithms.Algorithm;
|
||||
import com.auth0.jwt.interfaces.DecodedJWT;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import io.jsonwebtoken.Jwts;
|
||||
import io.jsonwebtoken.SignatureAlgorithm;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Base64;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
|
||||
|
|
@ -18,7 +23,7 @@ import java.util.Map;
|
|||
@Component(value = "OnlyOfficeJwtService")
|
||||
public class OnlyOfficeJwtService {
|
||||
|
||||
@Value("${onlyoffice.secret}")
|
||||
@Value("${only-office.secret}")
|
||||
private String jwtSecret;
|
||||
|
||||
// 生成 JWT Token
|
||||
|
|
@ -27,7 +32,7 @@ public class OnlyOfficeJwtService {
|
|||
return Jwts.builder()
|
||||
.setClaims(payload)
|
||||
.signWith(SignatureAlgorithm.HS256, jwtSecret.getBytes())
|
||||
.setExpiration(new Date(System.currentTimeMillis() + 3600000))
|
||||
.setExpiration(new Date(System.currentTimeMillis() + 36000000))
|
||||
.compact();
|
||||
}
|
||||
|
||||
|
|
@ -41,5 +46,33 @@ public class OnlyOfficeJwtService {
|
|||
}
|
||||
}
|
||||
|
||||
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
public String createToken(Object object) {
|
||||
Map<String, ?> payloadMap = (Map)this.objectMapper.convertValue(object, Map.class);
|
||||
return this.createToken(payloadMap, jwtSecret);
|
||||
}
|
||||
|
||||
public String createToken(Object object, String key) {
|
||||
Map<String, ?> payloadMap = (Map)this.objectMapper.convertValue(object, Map.class);
|
||||
return this.createToken(payloadMap, key);
|
||||
}
|
||||
|
||||
public String createToken(Map<String, ?> payloadMap, String key) {
|
||||
Algorithm algorithm = Algorithm.HMAC256(key);
|
||||
String token = JWT.create().withPayload(payloadMap).sign(algorithm);
|
||||
return token;
|
||||
}
|
||||
|
||||
public String verify(String token) {
|
||||
return this.verifyToken(token, jwtSecret);
|
||||
}
|
||||
|
||||
public String verifyToken(String token, String key) {
|
||||
Algorithm algorithm = Algorithm.HMAC256(key);
|
||||
Base64.Decoder decoder = Base64.getUrlDecoder();
|
||||
DecodedJWT jwt = JWT.require(algorithm).build().verify(token);
|
||||
return new String(decoder.decode(jwt.getPayload()));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
/*
|
||||
package com.bonus.file.service;
|
||||
|
||||
import com.bonus.common.core.domain.model.LoginUser;
|
||||
|
|
@ -14,20 +15,23 @@ import org.springframework.web.bind.annotation.CrossOrigin;
|
|||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.net.URLEncoder;
|
||||
import java.io.PrintWriter;
|
||||
import java.time.Duration;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
*/
|
||||
/**
|
||||
* @className:OnlyOfficeService
|
||||
* @author:cwchen
|
||||
* @date:2025-11-03-16:14
|
||||
* @version:1.0
|
||||
* @description:OnlyOffice 服务类
|
||||
*/
|
||||
*//*
|
||||
|
||||
@Service(value = "OnlyOfficeService")
|
||||
@Slf4j
|
||||
@CrossOrigin(origins = "*")
|
||||
|
|
@ -47,20 +51,14 @@ public class OnlyOfficeService {
|
|||
|
||||
private static final String DOCUMENT_CACHE_KEY = "document:";
|
||||
|
||||
public Map<String, Object> getConfigWithToken(String fileKey, String fileName, String mode) throws Exception {
|
||||
Map<String, Object> map = buildEditorConfig(fileKey, fileName, mode);
|
||||
public Map<String, Object> getConfigWithToken(String fileKey, String fileName, String type, String mode) throws Exception {
|
||||
Map<String, Object> map = buildEditorConfig(fileKey, fileName, type, mode);
|
||||
String token = generateJwtToken(map);
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> editorConfig = (Map<String, Object>) map.getOrDefault("editorConfig", new HashMap<>());
|
||||
// 添加token
|
||||
editorConfig.put("token", token);
|
||||
// 确保editorConfig被放回map中
|
||||
map.put("editorConfig", editorConfig);
|
||||
map.put("token", token);
|
||||
return map;
|
||||
}
|
||||
|
||||
public Map<String, Object> buildEditorConfig(String fileKey, String fileName, String mode) throws Exception {
|
||||
public Map<String, Object> buildEditorConfig(String fileKey, String fileName, String type, String mode) throws Exception {
|
||||
|
||||
// 清理文件名,只保留最终文件名
|
||||
String cleanFileName = extractFileName(fileName);
|
||||
|
|
@ -83,14 +81,14 @@ public class OnlyOfficeService {
|
|||
|
||||
// 用户信息
|
||||
Map<String, Object> user = new HashMap<>();
|
||||
user.put("id", getCurrentUserId());
|
||||
user.put("id", getCurrentUserId() + "");
|
||||
user.put("name", getCurrentUserName());
|
||||
|
||||
editorConfig.put("user", user);
|
||||
|
||||
// 自定义配置
|
||||
Map<String, Object> customization = new HashMap<>();
|
||||
customization.put("forcesave", true);
|
||||
customization.put("forcesave", false);
|
||||
customization.put("about", true);
|
||||
customization.put("feedback", true);
|
||||
customization.put("hideRightMenu", true);
|
||||
|
|
@ -100,18 +98,17 @@ public class OnlyOfficeService {
|
|||
config.put("document", document);
|
||||
config.put("documentType", getDocumentType(fileName));
|
||||
config.put("editorConfig", editorConfig);
|
||||
config.put("type", "embedded");
|
||||
config.put("type", type);
|
||||
config.put("width", "100%");
|
||||
config.put("height", "100%");
|
||||
// 缓存文档信息
|
||||
cacheDocumentInfo(fileKey, fileName);
|
||||
|
||||
// cacheDocumentInfo(fileKey, fileName);
|
||||
return config;
|
||||
}
|
||||
|
||||
public void handleCallback(OnlyOfficeCallback callback) {
|
||||
public void handleCallback(OnlyOfficeCallback callback, HttpServletResponse response) throws Exception {
|
||||
PrintWriter writer = response.getWriter();
|
||||
log.info("收到 OnlyOffice 回调, 状态: {}, Key: {}", callback.getStatus(), callback.getKey());
|
||||
|
||||
switch (callback.getStatus()) {
|
||||
case OnlyOfficeCallback.STATUS_READY_FOR_SAVE:
|
||||
case OnlyOfficeCallback.STATUS_FORCE_SAVE:
|
||||
|
|
@ -126,6 +123,7 @@ public class OnlyOfficeService {
|
|||
default:
|
||||
log.info("文档状态: {}", callback.getStatus());
|
||||
}
|
||||
writer.write("{\"error\":0}");
|
||||
}
|
||||
|
||||
private void saveDocumentFromCallback(OnlyOfficeCallback callback) {
|
||||
|
|
@ -171,7 +169,9 @@ public class OnlyOfficeService {
|
|||
}
|
||||
|
||||
private String buildCallbackUrl() {
|
||||
return "http://" + IpUtils.getHostIp() +":" + onlyOfficeConfig.getServerPort() + "/smartBid/documents/callback";
|
||||
String hostIp = IpUtils.getHostIp();
|
||||
// log.info("服务器ip:{}", hostIp);
|
||||
return "http://" + hostIp + ":" + onlyOfficeConfig.getServerPort() + "/smartBid/documents/callback";
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -198,20 +198,47 @@ public class OnlyOfficeService {
|
|||
case "odt":
|
||||
case "rtf":
|
||||
case "txt":
|
||||
return "text";
|
||||
case "docm":
|
||||
case "dot":
|
||||
case "dotx":
|
||||
case "dotm":
|
||||
case "fodt":
|
||||
case "ott":
|
||||
case "html":
|
||||
case "htm":
|
||||
case "mht":
|
||||
case "xml":
|
||||
return "word"; // 修改: text → word
|
||||
case "xlsx":
|
||||
case "xls":
|
||||
case "ods":
|
||||
case "csv":
|
||||
return "spreadsheet";
|
||||
case "xlsm":
|
||||
case "xlt":
|
||||
case "xltx":
|
||||
case "xltm":
|
||||
case "fods":
|
||||
case "ots":
|
||||
return "cell"; // 修改: spreadsheet → cell
|
||||
case "pptx":
|
||||
case "ppt":
|
||||
case "odp":
|
||||
return "presentation";
|
||||
case "pptm":
|
||||
case "pot":
|
||||
case "potx":
|
||||
case "potm":
|
||||
case "fodp":
|
||||
case "otp":
|
||||
return "slide"; // 修改: presentation → slide
|
||||
case "pdf":
|
||||
return "pdf";
|
||||
return "pdf"; // 保持不变
|
||||
case "vsdx":
|
||||
case "vssx":
|
||||
case "vsd":
|
||||
case "vss":
|
||||
return "diagram"; // 新增图表类型
|
||||
default:
|
||||
return "text";
|
||||
return "word"; // 修改: text → word
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -237,9 +264,12 @@ public class OnlyOfficeService {
|
|||
int lastIndex = Math.max(lastSlash, lastBackslash);
|
||||
return lastIndex >= 0 ? filePath.substring(lastIndex + 1) : filePath;
|
||||
}
|
||||
/**
|
||||
|
||||
*/
|
||||
/**
|
||||
* 生成 JWT Token(OnlyOffice 专用格式)
|
||||
*/
|
||||
*//*
|
||||
|
||||
private String generateJwtToken(Map<String, Object> config) {
|
||||
try {
|
||||
// OnlyOffice 需要的 payload 结构
|
||||
|
|
@ -266,3 +296,4 @@ public class OnlyOfficeService {
|
|||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -0,0 +1,191 @@
|
|||
package com.bonus.file.service;
|
||||
|
||||
import cn.hutool.crypto.digest.DigestUtil;
|
||||
import com.bonus.common.core.domain.AjaxResult;
|
||||
import com.bonus.common.core.domain.model.LoginUser;
|
||||
import com.bonus.common.domain.file.vo.Callback;
|
||||
import com.bonus.common.domain.file.vo.OnlyOfficeConfig;
|
||||
import com.bonus.common.domain.file.vo.ResourceFileVo;
|
||||
import com.bonus.common.utils.DocumentTypeUtils;
|
||||
import com.bonus.common.utils.SecurityUtils;
|
||||
import com.bonus.common.utils.StringUtils;
|
||||
import com.bonus.common.utils.ip.IpUtils;
|
||||
import com.bonus.file.config.MinioConfig;
|
||||
import io.minio.MinioClient;
|
||||
import io.minio.StatObjectArgs;
|
||||
import io.minio.StatObjectResponse;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.bind.annotation.CrossOrigin;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.net.URLEncoder;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* @className:OnlyOfficeService2
|
||||
* @author:cwchen
|
||||
* @date:2025-11-03-16:14
|
||||
* @version:1.0
|
||||
* @description:OnlyOffice 服务类
|
||||
*/
|
||||
@Service(value = "OnlyOfficeService2")
|
||||
@Slf4j
|
||||
@CrossOrigin(origins = "*")
|
||||
public class OnlyOfficeService2 {
|
||||
|
||||
@Resource(name = "FileUploadService")
|
||||
private FileUploadService fileUploadService;
|
||||
|
||||
@Resource(name = "OnlyOfficeJwtService")
|
||||
private OnlyOfficeJwtService onlyOfficeJwtService;
|
||||
|
||||
|
||||
@Resource(name = "OnlyOfficeConfig")
|
||||
private OnlyOfficeConfig onlyOfficeConfig;
|
||||
|
||||
@Resource(name = "CallBackService")
|
||||
private CallBackService callBackService;
|
||||
|
||||
@Autowired
|
||||
private MinioClient minioClient;
|
||||
|
||||
@Resource
|
||||
private MinioConfig minioConfig;
|
||||
|
||||
@Value("${server.port}")
|
||||
private String serverPort;
|
||||
|
||||
/**
|
||||
* 获取onlyoffice-config配置
|
||||
*
|
||||
* @param fileVo
|
||||
* @param type
|
||||
* @param mode
|
||||
* @return Map<String, Object>
|
||||
* @author cwchen
|
||||
* @date 2025/11/7 22:28
|
||||
*/
|
||||
public AjaxResult getOnlyOfficeConfig(ResourceFileVo fileVo, String type, String mode) throws Exception {
|
||||
OnlyOfficeConfig.Config config = onlyOfficeConfig.getConfig();
|
||||
OnlyOfficeConfig.Config configuration = new OnlyOfficeConfig.Config();
|
||||
OnlyOfficeConfig.Config.Document documentConfig = new OnlyOfficeConfig.Config.Document();
|
||||
// 生成唯一文件名
|
||||
String key = null;
|
||||
try {
|
||||
StatObjectResponse statted = minioClient.statObject(StatObjectArgs.builder()
|
||||
.bucket(minioConfig.getBucketName())
|
||||
.object(fileVo.getFilePath()).build());
|
||||
ZonedDateTime zonedDateTime = statted.lastModified();
|
||||
long timestamp = zonedDateTime.toInstant().toEpochMilli();
|
||||
key = DigestUtil.sha256Hex(fileVo.getFileName() + timestamp);
|
||||
} catch (Exception e) {
|
||||
key = UUID.randomUUID().toString();
|
||||
}
|
||||
documentConfig.setKey(key);
|
||||
// 编辑模式
|
||||
if (Objects.equals(mode, "edit")) {
|
||||
documentConfig.setTitle(fileVo.getFileName());
|
||||
} else { // 预览模式
|
||||
documentConfig.setTitle(StringUtils.format("{}({})", fileVo.getFileName(), "预览模式"));
|
||||
}
|
||||
documentConfig.setFileType(getFileExtension(fileVo.getFileName()));
|
||||
OnlyOfficeConfig.Config.Document.Permissions permissions = config.getDocument().getPermissions();
|
||||
if (Objects.equals(mode, "edit")) {
|
||||
permissions.setEdit(true);
|
||||
permissions.setReview(false);
|
||||
}
|
||||
documentConfig.setPermissions(permissions);
|
||||
|
||||
String fileUrl = fileUploadService.getFile(fileVo.getFilePath()).getFilePath();
|
||||
documentConfig.setUrl(fileUrl);
|
||||
|
||||
|
||||
OnlyOfficeConfig.Config.EditorConfig editorConfig = config.getEditorConfig();
|
||||
|
||||
// 生成回调函数地址
|
||||
String callbackUrl = buildCallbackUrl();
|
||||
String encodeFileUrl = URLEncoder.encode(fileVo.getFilePath(), "UTF-8");
|
||||
String newCallbackUrl = StringUtils.format("{}?fileUrl={}", callbackUrl, encodeFileUrl);
|
||||
editorConfig.setCallbackUrl(newCallbackUrl);
|
||||
|
||||
OnlyOfficeConfig.Config.EditorConfig.User user = editorConfig.getUser();
|
||||
user.setId(String.valueOf(getCurrentUserId()));
|
||||
user.setName(getCurrentUserName());
|
||||
editorConfig.setUser(user);
|
||||
configuration.setEditorConfig(editorConfig);
|
||||
configuration.setDocumentType(DocumentTypeUtils.getDocumentType(fileUrl));
|
||||
configuration.setDocument(documentConfig);
|
||||
|
||||
// 生成token
|
||||
HashMap<String, Object> claims = new HashMap<>();
|
||||
claims.put("document", documentConfig);
|
||||
claims.put("editorConfig", editorConfig);
|
||||
claims.put("documentType", DocumentTypeUtils.getDocumentType(fileUrl));
|
||||
claims.put("type", config.getType());
|
||||
String token = onlyOfficeJwtService.generateToken(claims);
|
||||
configuration.setToken(token);
|
||||
configuration.setType(config.getType());
|
||||
return AjaxResult.success(configuration);
|
||||
}
|
||||
|
||||
/**
|
||||
* onlyoffice回调函数
|
||||
* @param callback
|
||||
* @param fileUrl
|
||||
* @param request
|
||||
* @param response
|
||||
* @return String
|
||||
* @author cwchen
|
||||
* @date 2025/11/8 3:36
|
||||
*/
|
||||
public String handleCallback(Callback callback, String fileUrl, HttpServletRequest request, HttpServletResponse response) throws Exception {
|
||||
// 1.校验token
|
||||
String authorization = request.getHeader("Authorization");
|
||||
if (StringUtils.isEmpty(authorization)) {
|
||||
throw new RuntimeException("token校验失败");
|
||||
}
|
||||
// callback = callBackService.verifyCallback(callback, authorization);
|
||||
// 处理文件回调
|
||||
callBackService.processCallback(callback, fileUrl);
|
||||
// 校验callback参数
|
||||
return "{\"error\":\"0\"}";
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建回调url
|
||||
* @return String
|
||||
* @author cwchen
|
||||
* @date 2025/11/8 4:07
|
||||
*/
|
||||
private String buildCallbackUrl() {
|
||||
String hostIp = IpUtils.getHostIp();
|
||||
// log.info("服务器ip:{}", hostIp);
|
||||
return "http://" + hostIp + ":" + serverPort + "/smartBid/documents/callback";
|
||||
}
|
||||
|
||||
private Long getCurrentUserId() {
|
||||
// 获取用户ID
|
||||
return Optional.ofNullable(SecurityUtils.getLoginUser())
|
||||
.map(LoginUser::getUserId)
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
private String getCurrentUserName() {
|
||||
// 获取用户名
|
||||
return Optional.ofNullable(SecurityUtils.getLoginUser())
|
||||
.map(LoginUser::getUsername)
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
|
||||
private String getFileExtension(String fileName) {
|
||||
return fileName.substring(fileName.lastIndexOf(".") + 1).toLowerCase();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -90,4 +90,15 @@ public class SourceFileService {
|
|||
public void delResourceFileBybusinessId(List<ResourceFilePo> delFiles) {
|
||||
mapper.delResourceFileBybusinessId(delFiles);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据文件id获取文件对象
|
||||
* @param po
|
||||
* @return ResourceFileVo
|
||||
* @author cwchen
|
||||
* @date 2025/11/7 22:15
|
||||
*/
|
||||
public ResourceFileVo getFileById(ResourceFilePo po) {
|
||||
return mapper.getFileById(po);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -84,4 +84,13 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||
AND sys_resource_file.business_type = t.business_type
|
||||
)
|
||||
</update>
|
||||
|
||||
<!--根据文件id获取文件对象-->
|
||||
<select id="getFileById" resultType="com.bonus.common.domain.file.vo.ResourceFileVo">
|
||||
SELECT source_id AS sourceId,
|
||||
file_path AS filePath,
|
||||
source_file_name AS fileName
|
||||
FROM sys_resource_file
|
||||
WHERE source_id = #{sourceId} AND del_flag = '0'
|
||||
</select>
|
||||
</mapper>
|
||||
|
|
|
|||
|
|
@ -108,7 +108,7 @@ public class XssRequestWrapper extends HttpServletRequestWrapper {
|
|||
XSS_PATTERNS.add(Pattern.compile("cookie", Pattern.CASE_INSENSITIVE));
|
||||
|
||||
// 1. SQL注释模式
|
||||
XSS_PATTERNS.add(Pattern.compile("--", Pattern.CASE_INSENSITIVE)); // 单行注释
|
||||
// XSS_PATTERNS.add(Pattern.compile("--", Pattern.CASE_INSENSITIVE)); // 单行注释
|
||||
// XSS_PATTERNS.add(Pattern.compile("/\\*.*?\\*/", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL)); // 多行注释
|
||||
// XSS_PATTERNS.add(Pattern.compile("#", Pattern.CASE_INSENSITIVE)); // MySQL注释
|
||||
|
||||
|
|
|
|||
|
|
@ -53,6 +53,10 @@ public class TokenService
|
|||
@Value("${token.expireTime}")
|
||||
private int expireTime;
|
||||
|
||||
// onlyoffice忽略的回调函数访问路径
|
||||
@Value("${ignoreUrl.callBackUrl}")
|
||||
private String callBackUrl;
|
||||
|
||||
protected static final long MILLIS_SECOND = 1000;
|
||||
|
||||
protected static final long MILLIS_MINUTE = 60 * MILLIS_SECOND;
|
||||
|
|
@ -76,6 +80,9 @@ public class TokenService
|
|||
*/
|
||||
public LoginUser getLoginUser(HttpServletRequest request)
|
||||
{
|
||||
if(request.getRequestURI().contains(callBackUrl)){
|
||||
return null;
|
||||
}
|
||||
// 获取请求携带的令牌
|
||||
String token = getToken(request);
|
||||
if (StringUtils.isNotEmpty(token))
|
||||
|
|
@ -283,21 +290,35 @@ public class TokenService
|
|||
*/
|
||||
private String getToken(HttpServletRequest request)
|
||||
{
|
||||
// 1. 优先从请求头获取 token
|
||||
String token = request.getHeader(header);
|
||||
if (StringUtils.isNotEmpty(token) && token.startsWith(Constants.TOKEN_PREFIX))
|
||||
{
|
||||
token = token.replace(Constants.TOKEN_PREFIX, "");
|
||||
}
|
||||
|
||||
// 2. 如果请求头中没有 token,从参数中获取
|
||||
if (StringUtils.isEmpty(token)) {
|
||||
token = request.getParameter("token");
|
||||
// 如果参数中的 token 也有前缀,同样处理
|
||||
if (StringUtils.isNotEmpty(token) && token.startsWith(Constants.TOKEN_PREFIX))
|
||||
{
|
||||
token = token.replace(Constants.TOKEN_PREFIX, "");
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 如果 token 为空,直接返回
|
||||
if(StringUtils.isEmpty(token)){
|
||||
return token;
|
||||
}
|
||||
|
||||
// 4. 根据系统配置决定是否解密
|
||||
boolean systemConfigStatus = getSystemConfigStatus(CacheConstants.AUTH);
|
||||
String decryptToken = Sm4Utils.decrypt(token);
|
||||
if(!systemConfigStatus && Objects.equals(decryptToken, token)){
|
||||
return token;
|
||||
}else{
|
||||
boolean systemConfigStatus = getSystemConfigStatus(CacheConstants.AUTH);
|
||||
String decryptToken = Sm4Utils.decrypt(token);
|
||||
if(!systemConfigStatus && Objects.equals(decryptToken, token)){
|
||||
return token;
|
||||
}else{
|
||||
return decryptToken;
|
||||
}
|
||||
return decryptToken;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -426,6 +447,34 @@ public class TokenService
|
|||
return false;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 根据用户ID获取LoginUser对象(高效版本)
|
||||
*/
|
||||
public LoginUser getLoginUserByUuid(String uuid) {
|
||||
if (uuid == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
// 通过用户ID映射直接获取token
|
||||
String userIdTokenKey = getTokenKey(uuid);
|
||||
String token = redisCache.getCacheObject(userIdTokenKey);
|
||||
if (StringUtils.isEmpty(token)) {
|
||||
return null;
|
||||
}
|
||||
// 根据token获取LoginUser
|
||||
return getLoginUser(token);
|
||||
} catch (Exception e) {
|
||||
log.error("根据用户uuid获取LoginUser异常,uuid: {}", uuid, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户ID到token的映射key
|
||||
*/
|
||||
private String getUserIdTokenKey(Long userId) {
|
||||
return CacheConstants.LOGIN_TOKEN_KEY + userId;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取系统配置
|
||||
|
|
|
|||
Loading…
Reference in New Issue