Skip to content

Commit

Permalink
Merge pull request #94 from jamebal/ocr
Browse files Browse the repository at this point in the history
perf: 适配 onlyoffice JWT
  • Loading branch information
jamebal authored Jun 8, 2024
2 parents 7464cca + 4ed8d81 commit 96a5db4
Show file tree
Hide file tree
Showing 5 changed files with 246 additions and 10 deletions.
97 changes: 97 additions & 0 deletions src/main/java/com/jmal/clouddisk/office/OfficeConfigService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package com.jmal.clouddisk.office;

import cn.hutool.core.codec.Base62;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.KeyUtil;
import cn.hutool.crypto.symmetric.SymmetricAlgorithm;
import cn.hutool.crypto.symmetric.SymmetricCrypto;
import cn.hutool.jwt.JWT;
import cn.hutool.jwt.signers.JWTSigner;
import cn.hutool.jwt.signers.JWTSignerUtil;
import com.jmal.clouddisk.office.model.OfficeConfigDO;
import com.jmal.clouddisk.office.model.OfficeConfigDTO;
import lombok.RequiredArgsConstructor;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Service;

import java.util.Map;

@Service
@RequiredArgsConstructor
public class OfficeConfigService {

public final static String VO_KEY = "****************";

private final MongoTemplate mongoTemplate;
private volatile String officeServerSecret;

private String getOfficeServerSecret() {
if (officeServerSecret == null) {
synchronized (this) {
if (officeServerSecret == null) {
OfficeConfigDO officeConfigDO = mongoTemplate.findOne(new Query(), OfficeConfigDO.class);
if (officeConfigDO == null || officeConfigDO.getEncrypted() == null || officeConfigDO.getKey() == null) {
officeServerSecret = ""; // 如果没有找到配置或配置不完整,设置为空字符串
} else {
SymmetricCrypto symmetricCrypto = new SymmetricCrypto(SymmetricAlgorithm.AES, officeConfigDO.getKey().getBytes());
officeServerSecret = symmetricCrypto.decryptStr(officeConfigDO.getEncrypted());
}
}
}
}
return officeServerSecret;
}

public String createOfficeToken(final Map<String, Object> payloadClaims) {
String key = getOfficeServerSecret();
if (StrUtil.isBlank(key)) {
return "";
}
final JWTSigner signer = JWTSignerUtil.hs256(getOfficeServerSecret().getBytes());
JWT jwt = JWT.create().addPayloads(payloadClaims);
return jwt.sign(signer);
}

public OfficeConfigDTO getOfficeConfig() {
OfficeConfigDO officeConfigDO = mongoTemplate.findOne(new Query(), OfficeConfigDO.class);
if (officeConfigDO == null) {
return new OfficeConfigDTO();
}
return officeConfigDO.toOfficeConfigDTO();
}

public void setOfficeConfig(OfficeConfigDTO officeConfigDTO) {
OfficeConfigDO officeConfigDO = officeConfigDTO.toOfficeConfigDO();
Query query = new Query();
Update update = new Update()
.set("documentServer", officeConfigDO.getDocumentServer())
.set("callbackServer", officeConfigDO.getCallbackServer())
.set("tokenEnabled", officeConfigDO.getTokenEnabled())
.set("format", officeConfigDO.getFormat());
if (!VO_KEY.equals(officeConfigDTO.getSecret())) {
update.set("encrypted", officeConfigDO.getEncrypted());
update.set("key", officeConfigDO.getKey());
}
mongoTemplate.upsert(query, update, OfficeConfigDO.class);

// 重置缓存的密钥,使其在下次访问时重新加载
synchronized (this) {
if (VO_KEY.equals(officeConfigDTO.getSecret())) {
return;
}
if (StrUtil.isNotBlank(officeConfigDTO.getSecret())) {
officeServerSecret = officeConfigDTO.getSecret();
} else {
officeServerSecret = null;
}
}
}

public static String generateKey() {
byte[] keyBytes = KeyUtil.generateKey(SymmetricAlgorithm.AES.getValue(), 256).getEncoded();
String base62Key = Base62.encode(keyBytes);
return base62Key.length() > 32 ? base62Key.substring(0, 32) : base62Key;
}
}
54 changes: 45 additions & 9 deletions src/main/java/com/jmal/clouddisk/office/OfficeController.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,20 @@
import com.alibaba.fastjson.JSON;
import com.jmal.clouddisk.annotation.Permission;
import com.jmal.clouddisk.office.callbacks.CallbackHandler;
import com.jmal.clouddisk.office.model.OfficeConfigDTO;
import com.jmal.clouddisk.office.model.Track;
import com.jmal.clouddisk.service.Constants;
import com.jmal.clouddisk.service.impl.ShareServiceImpl;
import com.jmal.clouddisk.util.ResponseResult;
import com.jmal.clouddisk.util.ResultUtil;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import java.util.Map;

/**
* @author jmal
Expand All @@ -22,19 +26,51 @@
@Tag(name = "office文件管理")
@Slf4j
@RestController
@RequiredArgsConstructor
public class OfficeController {

@Autowired
CallbackHandler callbackHandler;
private final CallbackHandler callbackHandler;

private final ShareServiceImpl shareService;

private final OfficeConfigService officeConfigService;

@Operation(summary = "office 回调")
@PostMapping("/office/track")
@Permission("cloud:file:list")
public String track(@RequestParam(Constants.FILE_ID) String fileId, @RequestBody Track body){
public String track(@RequestParam(Constants.FILE_ID) String fileId, @RequestBody Track body) {
body.setFileId(fileId);
log.info("callbackHandler, {}", JSON.toJSONString(body));
int error = callbackHandler.handle(body);
return"{\"error\":" + error + "}";
return "{\"error\":" + error + "}";
}

@Operation(summary = "signature")
@PostMapping("/office/signature")
public ResponseResult<String> signature(@RequestBody Map<String, Object> config) {
return ResultUtil.success(officeConfigService.createOfficeToken(config));
}

@Operation(summary = "public signature")
@PostMapping("/public/office/signature")
public ResponseResult<String> signaturePublic(@RequestBody Map<String, Object> config, @RequestParam String shareId, @RequestParam String shareToken) {
shareService.validShare(shareToken, shareId);
return ResultUtil.success(officeConfigService.createOfficeToken(config));
}

@Operation(summary = "获取office配置")
@GetMapping("/office/config")
@Permission(value = "cloud:set:sync")
public ResponseResult<OfficeConfigDTO> getOfficeConfig() {
return ResultUtil.success(officeConfigService.getOfficeConfig());
}

@Operation(summary = "更新office配置")
@PutMapping("/office/config")
@Permission(value = "cloud:set:sync")
public ResponseResult<OfficeConfigDTO> putOfficeConfig(@RequestBody @Validated OfficeConfigDTO officeConfigDTO) {
officeConfigService.setOfficeConfig(officeConfigDTO);
return ResultUtil.success();
}

}
36 changes: 36 additions & 0 deletions src/main/java/com/jmal/clouddisk/office/model/OfficeConfigDO.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.jmal.clouddisk.office.model;

import com.jmal.clouddisk.office.OfficeConfigService;
import lombok.Data;
import org.springframework.data.mongodb.core.mapping.Document;

import java.util.List;

@Data
@Document(collection = "officeConfig")
public class OfficeConfigDO {

private String documentServer;

private String callbackServer;

private String encrypted;

private String key;

private Boolean tokenEnabled;

private List<String> format;

public OfficeConfigDTO toOfficeConfigDTO() {
OfficeConfigDTO officeConfigDTO = new OfficeConfigDTO();
officeConfigDTO.setDocumentServer(this.documentServer);
officeConfigDTO.setCallbackServer(this.callbackServer);
officeConfigDTO.setFormat(this.format);
officeConfigDTO.setTokenEnabled(this.tokenEnabled);
if (this.tokenEnabled) {
officeConfigDTO.setSecret(OfficeConfigService.VO_KEY);
}
return officeConfigDTO;
}
}
63 changes: 63 additions & 0 deletions src/main/java/com/jmal/clouddisk/office/model/OfficeConfigDTO.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package com.jmal.clouddisk.office.model;

import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.symmetric.SymmetricAlgorithm;
import cn.hutool.crypto.symmetric.SymmetricCrypto;
import com.jmal.clouddisk.office.OfficeConfigService;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.Valid;
import jakarta.validation.constraints.Pattern;
import lombok.Data;

import java.util.List;

@Data
@Valid
public class OfficeConfigDTO {

@Pattern(
regexp = "^(https?://)([\\w.-]+)(:[0-9]+)?(/.*)?$|^$",
message = "文档服务器地址格式不正确"
)
@Schema(name = "documentServer", title = "文档服务器地址", example = "http://localhost:8082/office/word")
private String documentServer;

@Schema(name = "secret", title = "密钥")
private String secret;

@Pattern(
regexp = "^(https?://)([\\w.-]+)(:[0-9]+)?(/.*)?$|^$",
message = "回调服务地址格式不正确"
)
@Schema(name = "callbackServer", title = "回调服务地址")
private String callbackServer;

@Schema(name = "tokenEnabled", title = "是否启用token", hidden = true)
public boolean tokenEnabled;

@Schema(name = "format", title = "默认关联的文件格式")
private List<String> format;

public OfficeConfigDO toOfficeConfigDO() {
OfficeConfigDO officeConfigDO = new OfficeConfigDO();
officeConfigDO.setDocumentServer(this.documentServer);
officeConfigDO.setCallbackServer(this.callbackServer);
officeConfigDO.setFormat(this.format);
boolean tokenEnabled = StrUtil.isNotBlank(this.secret);
officeConfigDO.setTokenEnabled(tokenEnabled);
if (OfficeConfigService.VO_KEY.equals(this.secret)) {
return officeConfigDO;
}
if (tokenEnabled) {
String key = OfficeConfigService.generateKey();
String encrypted = new SymmetricCrypto(SymmetricAlgorithm.AES, key.getBytes()).encryptHex(this.secret);
officeConfigDO.setKey(key);
officeConfigDO.setEncrypted(encrypted);
} else {
officeConfigDO.setKey(null);
officeConfigDO.setEncrypted(null);
}
return officeConfigDO;
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -229,10 +229,14 @@ private List<FileIntroVO> getFileDocuments(UploadApiParamDTO upload, Criteria...
BeanUtils.copyProperties(fileDocument, fileIntroVO);
return fileIntroVO;
}).toList();
pushMessage(upload.getUsername(), Constants.LOCAL_CHUNK_SIZE, Constants.UPLOADER_CHUNK_SIZE);
pushConfigInfo(upload);
return sortByFileName(upload, fileIntroVOList, order);
}

private void pushConfigInfo(UploadApiParamDTO upload) {
pushMessage(upload.getUsername(), Constants.LOCAL_CHUNK_SIZE, Constants.UPLOADER_CHUNK_SIZE);
}

/***
* 设置分页条件
* @return 排序条件
Expand Down

0 comments on commit 96a5db4

Please sign in to comment.