mirror of
				https://github.com/continew-org/continew-admin.git
				synced 2025-11-04 09:01:37 +08:00 
			
		
		
		
	fix: 存储管理及菜单管理功能优化 (#52)
存储管理功能优化: 1、私钥脱敏修改为注解/数据库敏感字段加密 2、兼容私钥脱敏修改场景下的数据回带 3、修复存储配置禁用情况下修改报错 || -> && 菜单管理优化: 1、非外链类型菜单兼容"/"路径
This commit is contained in:
		@@ -21,6 +21,7 @@ import lombok.Data;
 | 
				
			|||||||
import top.continew.admin.common.enums.DisEnableStatusEnum;
 | 
					import top.continew.admin.common.enums.DisEnableStatusEnum;
 | 
				
			||||||
import top.continew.admin.system.enums.StorageTypeEnum;
 | 
					import top.continew.admin.system.enums.StorageTypeEnum;
 | 
				
			||||||
import top.continew.starter.extension.crud.model.entity.BaseDO;
 | 
					import top.continew.starter.extension.crud.model.entity.BaseDO;
 | 
				
			||||||
 | 
					import top.continew.starter.security.crypto.annotation.FieldEncrypt;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import java.io.Serial;
 | 
					import java.io.Serial;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -55,11 +56,13 @@ public class StorageDO extends BaseDO {
 | 
				
			|||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Access Key(访问密钥)
 | 
					     * Access Key(访问密钥)
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
 | 
					    @FieldEncrypt
 | 
				
			||||||
    private String accessKey;
 | 
					    private String accessKey;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Secret Key(私有密钥)
 | 
					     * Secret Key(私有密钥)
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
 | 
					    @FieldEncrypt
 | 
				
			||||||
    private String secretKey;
 | 
					    private String secretKey;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -21,6 +21,7 @@ import lombok.Data;
 | 
				
			|||||||
import top.continew.admin.common.enums.DisEnableStatusEnum;
 | 
					import top.continew.admin.common.enums.DisEnableStatusEnum;
 | 
				
			||||||
import top.continew.admin.system.enums.StorageTypeEnum;
 | 
					import top.continew.admin.system.enums.StorageTypeEnum;
 | 
				
			||||||
import top.continew.starter.extension.crud.model.resp.BaseDetailResp;
 | 
					import top.continew.starter.extension.crud.model.resp.BaseDetailResp;
 | 
				
			||||||
 | 
					import top.continew.starter.security.mask.annotation.JsonMask;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import java.io.Serial;
 | 
					import java.io.Serial;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -71,14 +72,9 @@ public class StorageResp extends BaseDetailResp {
 | 
				
			|||||||
     * 私有密钥
 | 
					     * 私有密钥
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    @Schema(description = "私有密钥", example = "")
 | 
					    @Schema(description = "私有密钥", example = "")
 | 
				
			||||||
 | 
					    @JsonMask(left = 4, right = 3)
 | 
				
			||||||
    private String secretKey;
 | 
					    private String secretKey;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * 私有密钥加密串
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    @Schema(description = "私有密钥加密串", example = "")
 | 
					 | 
				
			||||||
    private String secretKeyEncrypted;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * 终端节点
 | 
					     * 终端节点
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
@@ -119,4 +115,5 @@ public class StorageResp extends BaseDetailResp {
 | 
				
			|||||||
    public Boolean getDisabled() {
 | 
					    public Boolean getDisabled() {
 | 
				
			||||||
        return this.getIsDefault();
 | 
					        return this.getIsDefault();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -22,6 +22,7 @@ import cn.hutool.core.util.StrUtil;
 | 
				
			|||||||
import cn.hutool.core.util.URLUtil;
 | 
					import cn.hutool.core.util.URLUtil;
 | 
				
			||||||
import jakarta.annotation.Resource;
 | 
					import jakarta.annotation.Resource;
 | 
				
			||||||
import lombok.RequiredArgsConstructor;
 | 
					import lombok.RequiredArgsConstructor;
 | 
				
			||||||
 | 
					import lombok.extern.slf4j.Slf4j;
 | 
				
			||||||
import org.dromara.x.file.storage.core.FileStorageProperties;
 | 
					import org.dromara.x.file.storage.core.FileStorageProperties;
 | 
				
			||||||
import org.dromara.x.file.storage.core.FileStorageService;
 | 
					import org.dromara.x.file.storage.core.FileStorageService;
 | 
				
			||||||
import org.dromara.x.file.storage.core.FileStorageServiceBuilder;
 | 
					import org.dromara.x.file.storage.core.FileStorageServiceBuilder;
 | 
				
			||||||
@@ -57,51 +58,33 @@ import java.util.concurrent.CopyOnWriteArrayList;
 | 
				
			|||||||
 */
 | 
					 */
 | 
				
			||||||
@Service
 | 
					@Service
 | 
				
			||||||
@RequiredArgsConstructor
 | 
					@RequiredArgsConstructor
 | 
				
			||||||
 | 
					@Slf4j
 | 
				
			||||||
public class StorageServiceImpl extends BaseServiceImpl<StorageMapper, StorageDO, StorageResp, StorageResp, StorageQuery, StorageReq> implements StorageService {
 | 
					public class StorageServiceImpl extends BaseServiceImpl<StorageMapper, StorageDO, StorageResp, StorageResp, StorageQuery, StorageReq> implements StorageService {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private final FileStorageService fileStorageService;
 | 
					    private final FileStorageService fileStorageService;
 | 
				
			||||||
    @Resource
 | 
					    @Resource
 | 
				
			||||||
    private FileService fileService;
 | 
					    private FileService fileService;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    protected void fill(Object obj) {
 | 
					 | 
				
			||||||
        super.fill(obj);
 | 
					 | 
				
			||||||
        if (obj instanceof StorageResp resp && StrUtil.isNotBlank(resp.getSecretKey())) {
 | 
					 | 
				
			||||||
            resp.setSecretKeyEncrypted(SecureUtils.encryptByRsaPublicKey(resp.getSecretKey()));
 | 
					 | 
				
			||||||
            resp.setSecretKey(StrUtil.hide(resp.getSecretKey(), 4, resp.getSecretKey().length() - 3));
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    protected void beforeAdd(StorageReq req) {
 | 
					    protected void beforeAdd(StorageReq req) {
 | 
				
			||||||
        decryptSecretKey(req);
 | 
					        decodeSecretKey(req, null);
 | 
				
			||||||
        CheckUtils.throwIf(Boolean.TRUE.equals(req.getIsDefault()) && this.isDefaultExists(null), "请先取消原有默认存储");
 | 
					        CheckUtils.throwIf(Boolean.TRUE.equals(req.getIsDefault()) && this.isDefaultExists(null), "请先取消原有默认存储");
 | 
				
			||||||
        String code = req.getCode();
 | 
					        String code = req.getCode();
 | 
				
			||||||
        CheckUtils.throwIf(this.isCodeExists(code, null), "新增失败,[{}] 已存在", code);
 | 
					        CheckUtils.throwIf(this.isCodeExists(code, null), "新增失败,[{}] 已存在", code);
 | 
				
			||||||
        this.load(req);
 | 
					        this.load(req);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private void decryptSecretKey(StorageReq req) {
 | 
					 | 
				
			||||||
        if (!StorageTypeEnum.S3.equals(req.getType())) {
 | 
					 | 
				
			||||||
            return;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        String secretKey = ExceptionUtils.exToNull(() -> SecureUtils.decryptByRsaPrivateKey(req.getSecretKey()));
 | 
					 | 
				
			||||||
        ValidationUtils.throwIfNull(secretKey, "密钥解密失败");
 | 
					 | 
				
			||||||
        req.setSecretKey(secretKey);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    protected void beforeUpdate(StorageReq req, Long id) {
 | 
					    protected void beforeUpdate(StorageReq req, Long id) {
 | 
				
			||||||
        decryptSecretKey(req);
 | 
					 | 
				
			||||||
        String code = req.getCode();
 | 
					        String code = req.getCode();
 | 
				
			||||||
        CheckUtils.throwIf(this.isCodeExists(code, id), "修改失败,[{}] 已存在", code);
 | 
					        CheckUtils.throwIf(this.isCodeExists(code, id), "修改失败,[{}] 已存在", code);
 | 
				
			||||||
        DisEnableStatusEnum newStatus = req.getStatus();
 | 
					        DisEnableStatusEnum newStatus = req.getStatus();
 | 
				
			||||||
        StorageDO oldStorage = super.getById(id);
 | 
					        StorageDO oldStorage = super.getById(id);
 | 
				
			||||||
        CheckUtils.throwIf(Boolean.TRUE.equals(oldStorage.getIsDefault()) && DisEnableStatusEnum.DISABLE
 | 
					        CheckUtils.throwIf(Boolean.TRUE.equals(oldStorage.getIsDefault()) && DisEnableStatusEnum.DISABLE
 | 
				
			||||||
            .equals(newStatus), "[{}] 是默认存储,不允许禁用", oldStorage.getName());
 | 
					            .equals(newStatus), "[{}] 是默认存储,不允许禁用", oldStorage.getName());
 | 
				
			||||||
 | 
					        decodeSecretKey(req, oldStorage);
 | 
				
			||||||
        DisEnableStatusEnum oldStatus = oldStorage.getStatus();
 | 
					        DisEnableStatusEnum oldStatus = oldStorage.getStatus();
 | 
				
			||||||
        if (DisEnableStatusEnum.ENABLE.equals(oldStatus) || DisEnableStatusEnum.DISABLE.equals(newStatus)) {
 | 
					        if (DisEnableStatusEnum.ENABLE.equals(oldStatus) && DisEnableStatusEnum.DISABLE.equals(newStatus)) {
 | 
				
			||||||
            this.unload(BeanUtil.copyProperties(oldStorage, StorageReq.class));
 | 
					            this.unload(BeanUtil.copyProperties(oldStorage, StorageReq.class));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        if (DisEnableStatusEnum.ENABLE.equals(newStatus)) {
 | 
					        if (DisEnableStatusEnum.ENABLE.equals(newStatus)) {
 | 
				
			||||||
@@ -113,6 +96,23 @@ public class StorageServiceImpl extends BaseServiceImpl<StorageMapper, StorageDO
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private void decodeSecretKey(StorageReq req, StorageDO storage) {
 | 
				
			||||||
 | 
					        if (!StorageTypeEnum.S3.equals(req.getType())) {
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 修改Storage时,如果SecretKey不修改,字段脱敏无法回带手动设置
 | 
				
			||||||
 | 
					        if ((StrUtil.isBlank(req.getSecretKey()) || req.getSecretKey().contains("*")) && StrUtil.isNotBlank(storage
 | 
				
			||||||
 | 
					            .getSecretKey())) {
 | 
				
			||||||
 | 
					            req.setSecretKey(storage.getSecretKey());
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        String secretKey = ExceptionUtils.exToNull(() -> SecureUtils.decryptByRsaPrivateKey(req.getSecretKey()));
 | 
				
			||||||
 | 
					        ValidationUtils.throwIfNull(secretKey, "私有密钥解密失败");
 | 
				
			||||||
 | 
					        req.setSecretKey(secretKey);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    protected void beforeDelete(List<Long> ids) {
 | 
					    protected void beforeDelete(List<Long> ids) {
 | 
				
			||||||
        CheckUtils.throwIf(fileService.countByStorageIds(ids) > 0, "所选存储存在文件关联,请删除文件后重试");
 | 
					        CheckUtils.throwIf(fileService.countByStorageIds(ids) > 0, "所选存储存在文件关联,请删除文件后重试");
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -18,6 +18,7 @@ package top.continew.admin.webapi.system;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import cn.dev33.satoken.annotation.SaCheckPermission;
 | 
					import cn.dev33.satoken.annotation.SaCheckPermission;
 | 
				
			||||||
import cn.hutool.core.util.ObjectUtil;
 | 
					import cn.hutool.core.util.ObjectUtil;
 | 
				
			||||||
 | 
					import cn.hutool.core.util.StrUtil;
 | 
				
			||||||
import io.swagger.v3.oas.annotations.tags.Tag;
 | 
					import io.swagger.v3.oas.annotations.tags.Tag;
 | 
				
			||||||
import org.springframework.validation.annotation.Validated;
 | 
					import org.springframework.validation.annotation.Validated;
 | 
				
			||||||
import org.springframework.web.bind.annotation.PathVariable;
 | 
					import org.springframework.web.bind.annotation.PathVariable;
 | 
				
			||||||
@@ -27,6 +28,7 @@ import top.continew.admin.system.model.query.MenuQuery;
 | 
				
			|||||||
import top.continew.admin.system.model.req.MenuReq;
 | 
					import top.continew.admin.system.model.req.MenuReq;
 | 
				
			||||||
import top.continew.admin.system.model.resp.MenuResp;
 | 
					import top.continew.admin.system.model.resp.MenuResp;
 | 
				
			||||||
import top.continew.admin.system.service.MenuService;
 | 
					import top.continew.admin.system.service.MenuService;
 | 
				
			||||||
 | 
					import top.continew.starter.core.constant.StringConstants;
 | 
				
			||||||
import top.continew.starter.core.util.URLUtils;
 | 
					import top.continew.starter.core.util.URLUtils;
 | 
				
			||||||
import top.continew.starter.core.util.validate.ValidationUtils;
 | 
					import top.continew.starter.core.util.validate.ValidationUtils;
 | 
				
			||||||
import top.continew.starter.extension.crud.annotation.CrudRequestMapping;
 | 
					import top.continew.starter.extension.crud.annotation.CrudRequestMapping;
 | 
				
			||||||
@@ -69,6 +71,11 @@ public class MenuController extends BaseController<MenuService, MenuResp, MenuRe
 | 
				
			|||||||
        Boolean isExternal = ObjectUtil.defaultIfNull(req.getIsExternal(), false);
 | 
					        Boolean isExternal = ObjectUtil.defaultIfNull(req.getIsExternal(), false);
 | 
				
			||||||
        String path = req.getPath();
 | 
					        String path = req.getPath();
 | 
				
			||||||
        ValidationUtils.throwIf(isExternal && !URLUtils.isHttpUrl(path), "路由地址格式错误,请以 http:// 或 https:// 开头");
 | 
					        ValidationUtils.throwIf(isExternal && !URLUtils.isHttpUrl(path), "路由地址格式错误,请以 http:// 或 https:// 开头");
 | 
				
			||||||
        ValidationUtils.throwIf(!isExternal && URLUtils.isHttpUrl(path), "路由地址格式错误");
 | 
					        if (!isExternal) {
 | 
				
			||||||
 | 
					            ValidationUtils.throwIf(URLUtils.isHttpUrl(path), "路由地址格式错误");
 | 
				
			||||||
 | 
					            req.setPath(StrUtil.prependIfMissing(req.getPath(), StringConstants.SLASH));
 | 
				
			||||||
 | 
					            req.setName(StrUtil.removePrefix(req.getName(), StringConstants.SLASH));
 | 
				
			||||||
 | 
					            req.setComponent(StrUtil.removePrefix(req.getComponent(), StringConstants.SLASH));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user