mirror of
https://github.com/continew-org/continew-admin.git
synced 2025-09-12 05:01:39 +08:00
refactor(open): 重构及优化应用管理代码
This commit is contained in:
@@ -26,6 +26,7 @@ import top.continew.starter.core.util.validate.ValidationUtils;
|
||||
import top.continew.starter.security.crypto.autoconfigure.CryptoProperties;
|
||||
import top.continew.starter.security.crypto.encryptor.AesEncryptor;
|
||||
import top.continew.starter.security.crypto.encryptor.IEncryptor;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@@ -40,6 +41,18 @@ public class SecureUtils {
|
||||
private SecureUtils() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 公钥加密
|
||||
*
|
||||
* @param data 要加密的内容
|
||||
* @return 加密后的内容
|
||||
*/
|
||||
public static String encryptByRsaPublicKey(String data) {
|
||||
String publicKey = RsaProperties.PUBLIC_KEY;
|
||||
ValidationUtils.throwIfBlank(publicKey, "请配置 RSA 公钥");
|
||||
return encryptByRsaPublicKey(data, publicKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* 私钥解密
|
||||
*
|
||||
@@ -52,6 +65,17 @@ public class SecureUtils {
|
||||
return decryptByRsaPrivateKey(data, privateKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* 公钥加密
|
||||
*
|
||||
* @param data 要加密的内容
|
||||
* @param publicKey 公钥
|
||||
* @return 加密后的内容
|
||||
*/
|
||||
public static String encryptByRsaPublicKey(String data, String publicKey) {
|
||||
return new String(SecureUtil.rsa(null, publicKey).encrypt(data, KeyType.PublicKey));
|
||||
}
|
||||
|
||||
/**
|
||||
* 私钥解密
|
||||
*
|
||||
|
@@ -16,19 +16,20 @@
|
||||
|
||||
package top.continew.admin.open.model.entity;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.time.*;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
|
||||
import lombok.Data;
|
||||
import top.continew.admin.common.enums.DisEnableStatusEnum;
|
||||
import top.continew.starter.extension.crud.model.entity.BaseDO;
|
||||
import top.continew.starter.security.crypto.annotation.FieldEncrypt;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 应用实体
|
||||
*
|
||||
* @author chengzi
|
||||
* @author Charles7c
|
||||
* @since 2024/10/17 16:03
|
||||
*/
|
||||
@Data
|
||||
@@ -39,42 +40,34 @@ public class AppDO extends BaseDO {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* ID
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 应用名称
|
||||
* 名称
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* APPKEY
|
||||
* Access Key(访问密钥)
|
||||
*/
|
||||
private String appKey;
|
||||
@FieldEncrypt
|
||||
private String accessKey;
|
||||
|
||||
/**
|
||||
* APPSECRET
|
||||
* Secret Key(私有密钥)
|
||||
*/
|
||||
private String appSecret;
|
||||
|
||||
/**
|
||||
* 应用状态
|
||||
*/
|
||||
private String status;
|
||||
@FieldEncrypt
|
||||
private String secretKey;
|
||||
|
||||
/**
|
||||
* 失效时间
|
||||
*/
|
||||
private LocalDateTime expirationTime;
|
||||
private LocalDateTime expireTime;
|
||||
|
||||
/**
|
||||
* 应用描述
|
||||
* 描述
|
||||
*/
|
||||
private String appDesc;
|
||||
private String description;
|
||||
|
||||
/**
|
||||
* secret查看状态
|
||||
* 状态
|
||||
*/
|
||||
private String secretStatus;
|
||||
private DisEnableStatusEnum status;
|
||||
}
|
@@ -16,20 +16,19 @@
|
||||
|
||||
package top.continew.admin.open.model.query;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
import lombok.Data;
|
||||
import top.continew.starter.data.core.annotation.Query;
|
||||
import top.continew.starter.data.core.enums.QueryType;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 应用查询条件
|
||||
*
|
||||
* @author chengzi
|
||||
* @author Charles7c
|
||||
* @since 2024/10/17 16:03
|
||||
*/
|
||||
@Data
|
||||
@@ -40,16 +39,9 @@ public class AppQuery implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 应用名称
|
||||
* 关键词
|
||||
*/
|
||||
@Schema(description = "应用名称")
|
||||
@Query(type = QueryType.LIKE)
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* APPKEY
|
||||
*/
|
||||
@Schema(description = "APPKEY")
|
||||
@Query(type = QueryType.LIKE)
|
||||
private String appKey;
|
||||
@Schema(description = "关键词", example = "应用1")
|
||||
@Query(columns = {"name", "description"}, type = QueryType.LIKE)
|
||||
private String description;
|
||||
}
|
@@ -16,67 +16,68 @@
|
||||
|
||||
package top.continew.admin.open.model.req;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.time.*;
|
||||
|
||||
import jakarta.validation.constraints.*;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
import jakarta.validation.constraints.Future;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.Data;
|
||||
import org.hibernate.validator.constraints.Length;
|
||||
|
||||
import top.continew.admin.common.enums.DisEnableStatusEnum;
|
||||
import top.continew.starter.extension.crud.model.req.BaseReq;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 创建或修改应用信息
|
||||
* 创建或修改应用参数
|
||||
*
|
||||
* @author chengzi
|
||||
* @author Charles7c
|
||||
* @since 2024/10/17 16:03
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "创建或修改应用信息")
|
||||
@Schema(description = "创建或修改应用参数")
|
||||
public class AppReq extends BaseReq {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 应用名称
|
||||
* 名称
|
||||
*/
|
||||
@Schema(description = "应用名称")
|
||||
@NotBlank(message = "应用名称不能为空")
|
||||
@Length(max = 255, message = "应用名称长度不能超过 {max} 个字符")
|
||||
@Schema(description = "名称", example = "应用1")
|
||||
@NotBlank(message = "名称不能为空")
|
||||
@Length(max = 100, message = "名称长度不能超过 {max} 个字符")
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* APPKEY
|
||||
*/
|
||||
@Schema(description = "应用密钥")
|
||||
@NotBlank(message = "应用密钥不能为空")
|
||||
@Length(max = 255, message = "应用密钥长度不能超过 {max} 个字符")
|
||||
private String appKey;
|
||||
|
||||
/**
|
||||
* 应用状态
|
||||
*/
|
||||
@Schema(description = "应用状态")
|
||||
@NotBlank(message = "应用状态不能为空")
|
||||
@Length(max = 255, message = "应用状态长度不能超过 {max} 个字符")
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* 失效时间
|
||||
*/
|
||||
@Schema(description = "失效时间")
|
||||
@NotNull(message = "失效时间不能为空")
|
||||
private LocalDateTime expirationTime;
|
||||
@Schema(description = "失效时间", example = "2023-08-08 23:59:59", type = "string")
|
||||
@Future(message = "失效时间必须是未来时间")
|
||||
private LocalDateTime expireTime;
|
||||
|
||||
/**
|
||||
* 应用描述
|
||||
* 描述
|
||||
*/
|
||||
@Schema(description = "应用描述")
|
||||
@Length(max = 255, message = "应用描述长度不能超过 {max} 个字符")
|
||||
private String appDesc;
|
||||
@Schema(description = "描述", example = "应用1描述信息")
|
||||
@Length(max = 200, message = "描述长度不能超过 {max} 个字符")
|
||||
private String description;
|
||||
|
||||
/**
|
||||
* 状态
|
||||
*/
|
||||
@Schema(description = "状态", example = "1")
|
||||
private DisEnableStatusEnum status;
|
||||
|
||||
/**
|
||||
* Access Key(访问密钥)
|
||||
*/
|
||||
@Schema(hidden = true)
|
||||
private String accessKey;
|
||||
|
||||
/**
|
||||
* Secret Key(密钥)
|
||||
*/
|
||||
@Schema(hidden = true)
|
||||
private String secretKey;
|
||||
}
|
@@ -16,22 +16,22 @@
|
||||
|
||||
package top.continew.admin.open.model.resp;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.time.*;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
||||
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
|
||||
import com.alibaba.excel.annotation.ExcelProperty;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import top.continew.admin.common.enums.DisEnableStatusEnum;
|
||||
import top.continew.starter.extension.crud.model.resp.BaseDetailResp;
|
||||
import top.continew.starter.file.excel.converter.ExcelBaseEnumConverter;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 应用详情信息
|
||||
*
|
||||
* @author chengzi
|
||||
* @author Charles7c
|
||||
* @since 2024/10/17 16:03
|
||||
*/
|
||||
@Data
|
||||
@@ -43,37 +43,37 @@ public class AppDetailResp extends BaseDetailResp {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 应用名称
|
||||
* 名称
|
||||
*/
|
||||
@Schema(description = "应用名称")
|
||||
@ExcelProperty(value = "应用名称")
|
||||
@Schema(description = "名称", example = "应用1")
|
||||
@ExcelProperty(value = "名称", order = 2)
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 应用密钥
|
||||
* Access Key(访问密钥)
|
||||
*/
|
||||
@Schema(description = "应用密钥")
|
||||
@ExcelProperty(value = "应用密钥")
|
||||
private String appKey;
|
||||
|
||||
/**
|
||||
* 应用状态
|
||||
*/
|
||||
@Schema(description = "应用状态")
|
||||
@ExcelProperty(value = "应用状态")
|
||||
private String status;
|
||||
@Schema(description = "Access Key(访问密钥)", example = "YjUyMGJjYjIxNTE0NDAxMWE1NmRiY2")
|
||||
@ExcelProperty(value = "Access Key", order = 3)
|
||||
private String accessKey;
|
||||
|
||||
/**
|
||||
* 失效时间
|
||||
*/
|
||||
@Schema(description = "失效时间")
|
||||
@ExcelProperty(value = "失效时间")
|
||||
private LocalDateTime expirationTime;
|
||||
@Schema(description = "失效时间", example = "2023-08-08 08:08:08", type = "string")
|
||||
@ExcelProperty(value = "失效时间", order = 4)
|
||||
private LocalDateTime expireTime;
|
||||
|
||||
/**
|
||||
* 应用描述
|
||||
* 状态
|
||||
*/
|
||||
@Schema(description = "应用描述")
|
||||
@ExcelProperty(value = "应用描述")
|
||||
private String appDesc;
|
||||
@Schema(description = "状态", example = "1")
|
||||
@ExcelProperty(value = "状态", converter = ExcelBaseEnumConverter.class, order = 5)
|
||||
private DisEnableStatusEnum status;
|
||||
|
||||
/**
|
||||
* 描述
|
||||
*/
|
||||
@Schema(description = "描述", example = "应用1描述信息")
|
||||
@ExcelProperty(value = "描述", order = 6)
|
||||
private String description;
|
||||
}
|
@@ -16,56 +16,55 @@
|
||||
|
||||
package top.continew.admin.open.model.resp;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.time.*;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import top.continew.admin.common.enums.DisEnableStatusEnum;
|
||||
import top.continew.starter.extension.crud.model.resp.BaseDetailResp;
|
||||
|
||||
import top.continew.starter.extension.crud.model.resp.BaseResp;
|
||||
import java.io.Serial;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 应用信息
|
||||
*
|
||||
* @author chengzi
|
||||
* @author Charles7c
|
||||
* @since 2024/10/17 16:03
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "应用信息")
|
||||
public class AppResp extends BaseResp {
|
||||
public class AppResp extends BaseDetailResp {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 应用名称
|
||||
* 名称
|
||||
*/
|
||||
@Schema(description = "应用名称")
|
||||
@Schema(description = "名称", example = "应用1")
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* APPKEY
|
||||
* Access Key(访问密钥)
|
||||
*/
|
||||
@Schema(description = "应用密钥")
|
||||
private String appKey;
|
||||
|
||||
/**
|
||||
* 应用状态
|
||||
*/
|
||||
@Schema(description = "应用状态")
|
||||
private String status;
|
||||
@Schema(description = "Access Key(访问密钥)", example = "YjUyMGJjYjIxNTE0NDAxMWE1NmRiY2")
|
||||
private String accessKey;
|
||||
|
||||
/**
|
||||
* 失效时间
|
||||
*/
|
||||
@Schema(description = "失效时间")
|
||||
private LocalDateTime expirationTime;
|
||||
@Schema(description = "失效时间", example = "2023-08-08 08:08:08", type = "string")
|
||||
private LocalDateTime expireTime;
|
||||
|
||||
/**
|
||||
* 应用描述
|
||||
* 状态
|
||||
*/
|
||||
@Schema(description = "应用描述")
|
||||
private String appDesc;
|
||||
@Schema(description = "状态", example = "1")
|
||||
private DisEnableStatusEnum status;
|
||||
|
||||
/**
|
||||
* 描述
|
||||
*/
|
||||
@Schema(description = "描述", example = "应用1描述信息")
|
||||
private String description;
|
||||
}
|
@@ -20,28 +20,31 @@ import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 应用密钥/密码信息
|
||||
* 应用密钥信息
|
||||
*
|
||||
* @author chengzi
|
||||
* @author Charles7c
|
||||
* @since 2024/10/17 16:03
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "应用密钥/密码信息")
|
||||
public class AppSecretGetResp {
|
||||
@Schema(description = "应用密钥信息")
|
||||
public class AppSecretResp implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 应用密钥
|
||||
* Access Key(访问密钥)
|
||||
*/
|
||||
@Schema(description = "应用密钥")
|
||||
private String appKey;
|
||||
@Schema(description = "Access Key(访问密钥)", example = "YjUyMGJjYjIxNTE0NDAxMWE1NmRiY2")
|
||||
private String accessKey;
|
||||
|
||||
/**
|
||||
* 应用密码
|
||||
* Secret Key(私有密钥)
|
||||
*/
|
||||
@Schema(description = "应用密码")
|
||||
private String appSecret;
|
||||
@Schema(description = "Secret Key(私有密钥)", example = "")
|
||||
private String secretKey;
|
||||
}
|
@@ -16,71 +16,58 @@
|
||||
|
||||
package top.continew.admin.open.service;
|
||||
|
||||
import top.continew.admin.open.model.resp.AppSecretGetResp;
|
||||
import top.continew.starter.extension.crud.service.BaseService;
|
||||
import top.continew.admin.open.model.query.AppQuery;
|
||||
import top.continew.admin.open.model.req.AppReq;
|
||||
import top.continew.admin.open.model.resp.AppDetailResp;
|
||||
import top.continew.admin.open.model.resp.AppResp;
|
||||
import top.continew.admin.open.model.resp.AppSecretResp;
|
||||
import top.continew.starter.extension.crud.service.BaseService;
|
||||
|
||||
/**
|
||||
* 应用业务接口
|
||||
*
|
||||
* @author chengzi
|
||||
* @author Charles7c
|
||||
* @since 2024/10/17 16:03
|
||||
*/
|
||||
public interface AppService extends BaseService<AppResp, AppDetailResp, AppQuery, AppReq> {
|
||||
/**
|
||||
* 根据ID查询应用密码
|
||||
*
|
||||
* @param id ID
|
||||
* @return 应用密码
|
||||
*/
|
||||
AppSecretGetResp getAppSecretById(Long id);
|
||||
|
||||
/**
|
||||
* 根据ID重置应用密码查看状态
|
||||
* 获取密钥
|
||||
*
|
||||
* @param id ID
|
||||
* @return 密钥信息
|
||||
*/
|
||||
AppSecretResp getSecret(Long id);
|
||||
|
||||
/**
|
||||
* 重置密钥
|
||||
*
|
||||
* @param id ID
|
||||
*/
|
||||
void resetAppSecretStatusById(Long id, String status);
|
||||
void resetSecret(Long id);
|
||||
|
||||
/**
|
||||
* 根据应用密钥重置应用密码查看状态
|
||||
* 根据 Access Key 获取 Secret Key
|
||||
*
|
||||
* @param appKey 应用密钥
|
||||
* @param accessKey Access Key
|
||||
* @return Secret Key
|
||||
*/
|
||||
void resetAppSecretStatusByAppkey(String appKey, String status);
|
||||
String getSecretKeyByAccessKey(String accessKey);
|
||||
|
||||
/**
|
||||
* 根据ID刷新应用密码
|
||||
* 判断应用是否存在
|
||||
*
|
||||
* @param id ID
|
||||
*/
|
||||
void refreshAppSecretByID(Long id);
|
||||
|
||||
/**
|
||||
* 根据应用密钥获取应用密码
|
||||
*
|
||||
* @param appKey 应用密钥
|
||||
* @return 应用密码
|
||||
*/
|
||||
String getAppSecretByAppKey(String appKey);
|
||||
|
||||
/**
|
||||
* 判断应用密钥是否存在
|
||||
*
|
||||
* @param appKey 应用密钥
|
||||
* @param accessKey Access Key
|
||||
* @return 是否存在(true:存在;false:不存在)
|
||||
*/
|
||||
boolean isExistAppKey(String appKey);
|
||||
boolean isAppExists(String accessKey);
|
||||
|
||||
/**
|
||||
* 判断应用密钥是否过期
|
||||
*
|
||||
* @param appKey 应用密钥
|
||||
* @param accessKey Access Key
|
||||
* @return 是否过期(true:已过期;false:未过期)
|
||||
*/
|
||||
boolean isExpireAppKey(String appKey);
|
||||
|
||||
boolean isAppSecretExpired(String accessKey);
|
||||
}
|
@@ -16,22 +16,22 @@
|
||||
|
||||
package top.continew.admin.open.service.impl;
|
||||
|
||||
import cn.hutool.core.codec.Base64;
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.date.LocalDateTimeUtil;
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import top.continew.admin.open.model.resp.AppSecretGetResp;
|
||||
import top.continew.starter.extension.crud.service.impl.BaseServiceImpl;
|
||||
import top.continew.admin.open.mapper.AppMapper;
|
||||
import top.continew.admin.open.model.entity.AppDO;
|
||||
import top.continew.admin.open.model.query.AppQuery;
|
||||
import top.continew.admin.open.model.req.AppReq;
|
||||
import top.continew.admin.open.model.resp.AppDetailResp;
|
||||
import top.continew.admin.open.model.resp.AppResp;
|
||||
import top.continew.admin.open.model.resp.AppSecretResp;
|
||||
import top.continew.admin.open.service.AppService;
|
||||
import top.continew.starter.core.constant.StringConstants;
|
||||
import top.continew.starter.extension.crud.service.impl.BaseServiceImpl;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@@ -39,64 +39,76 @@ import java.time.LocalDateTime;
|
||||
* 应用业务实现
|
||||
*
|
||||
* @author chengzi
|
||||
* @author Charles7c
|
||||
* @since 2024/10/17 16:03
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class AppServiceImpl extends BaseServiceImpl<AppMapper, AppDO, AppResp, AppDetailResp, AppQuery, AppReq> implements AppService {
|
||||
|
||||
// 已激活
|
||||
private final static String APP_ENABLED_KEY = "1";
|
||||
// 未激活
|
||||
private final static String APP_DISABLED_KEY = "0";
|
||||
@Override
|
||||
public void beforeAdd(AppReq req) {
|
||||
req.setAccessKey(Base64.encode(IdUtil.fastSimpleUUID())
|
||||
.replace(StringConstants.SLASH, StringConstants.EMPTY)
|
||||
.replace("+", StringConstants.EMPTY)
|
||||
.substring(0, 30));
|
||||
req.setSecretKey(this.generateSecret());
|
||||
}
|
||||
|
||||
@Override
|
||||
public AppSecretGetResp getAppSecretById(Long id) {
|
||||
AppDO app = baseMapper.lambdaQuery().eq(AppDO::getId, id).one();
|
||||
String appSecret = "********";
|
||||
if (app.getSecretStatus().equals(APP_DISABLED_KEY)) {
|
||||
appSecret = app.getAppSecret();
|
||||
this.resetAppSecretStatusById(id, APP_ENABLED_KEY);
|
||||
public AppSecretResp getSecret(Long id) {
|
||||
AppDO app = super.getById(id);
|
||||
AppSecretResp appSecretResp = new AppSecretResp();
|
||||
appSecretResp.setAccessKey(app.getAccessKey());
|
||||
appSecretResp.setSecretKey(app.getSecretKey());
|
||||
return appSecretResp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetSecret(Long id) {
|
||||
super.getById(id);
|
||||
AppDO app = new AppDO();
|
||||
app.setSecretKey(this.generateSecret());
|
||||
baseMapper.update(app, Wrappers.lambdaQuery(AppDO.class).eq(AppDO::getId, id));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSecretKeyByAccessKey(String accessKey) {
|
||||
return baseMapper.lambdaQuery()
|
||||
.select(AppDO::getSecretKey)
|
||||
.eq(AppDO::getAccessKey, accessKey)
|
||||
.oneOpt()
|
||||
.map(AppDO::getSecretKey)
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAppExists(String accessKey) {
|
||||
return baseMapper.lambdaQuery().eq(AppDO::getAccessKey, accessKey).exists();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAppSecretExpired(String accessKey) {
|
||||
LocalDateTime expireTime = baseMapper.lambdaQuery()
|
||||
.select(AppDO::getExpireTime)
|
||||
.eq(AppDO::getAccessKey, accessKey)
|
||||
.oneOpt()
|
||||
.map(AppDO::getExpireTime)
|
||||
.orElse(null);
|
||||
if (expireTime == null) {
|
||||
return false;
|
||||
}
|
||||
AppSecretGetResp appSecretGetResp = new AppSecretGetResp();
|
||||
appSecretGetResp.setAppKey(app.getAppKey());
|
||||
appSecretGetResp.setAppSecret(appSecret);
|
||||
return appSecretGetResp;
|
||||
return expireTime.isBefore(DateUtil.date().toLocalDateTime());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetAppSecretStatusById(Long id, String status) {
|
||||
baseMapper.lambdaUpdate().set(AppDO::getSecretStatus, status).eq(AppDO::getId, id).update();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetAppSecretStatusByAppkey(String appKey, String status) {
|
||||
baseMapper.lambdaUpdate().set(AppDO::getSecretStatus, status).eq(AppDO::getAppKey, appKey).update();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refreshAppSecretByID(Long id) {
|
||||
baseMapper.lambdaUpdate().set(AppDO::getAppSecret, IdUtil.simpleUUID()).eq(AppDO::getId, id).update();
|
||||
this.resetAppSecretStatusById(id, APP_DISABLED_KEY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAppSecretByAppKey(String appKey) {
|
||||
return baseMapper.lambdaQuery().select(AppDO::getAppSecret).eq(AppDO::getAppKey, appKey).one().getAppSecret();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isExistAppKey(String appKey) {
|
||||
return baseMapper.lambdaQuery().eq(AppDO::getAppKey, appKey).exists();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isExpireAppKey(String appKey) {
|
||||
LocalDateTime expirationTime = baseMapper.lambdaQuery()
|
||||
.select(AppDO::getExpirationTime)
|
||||
.eq(AppDO::getAppKey, appKey)
|
||||
.one()
|
||||
.getExpirationTime();
|
||||
return expirationTime.isBefore(LocalDateTimeUtil.of(DateUtil.date()));
|
||||
/**
|
||||
* 生成密钥
|
||||
*
|
||||
* @return 密钥
|
||||
*/
|
||||
private String generateSecret() {
|
||||
return Base64.encode(IdUtil.fastSimpleUUID())
|
||||
.replace(StringConstants.SLASH, StringConstants.EMPTY)
|
||||
.replace("+", StringConstants.EMPTY);
|
||||
}
|
||||
}
|
@@ -28,12 +28,18 @@ import java.util.TreeMap;
|
||||
|
||||
import static cn.dev33.satoken.SaManager.log;
|
||||
|
||||
/**
|
||||
* API 参数签名算法
|
||||
*
|
||||
* @author chengzi
|
||||
* @since 2024/10/17 16:03
|
||||
*/
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class OpenSignTemplate extends SaSignTemplate {
|
||||
|
||||
private final AppService appService;
|
||||
public static String appKey = "appkey";
|
||||
public static final String ACCESS_KEY = "accessKey";
|
||||
|
||||
@Override
|
||||
public void checkParamMap(Map<String, String> paramMap) {
|
||||
@@ -41,19 +47,19 @@ public class OpenSignTemplate extends SaSignTemplate {
|
||||
String timestampValue = paramMap.get(timestamp);
|
||||
String nonceValue = paramMap.get(nonce);
|
||||
String signValue = paramMap.get(sign);
|
||||
String appKeyValue = paramMap.get(appKey);
|
||||
String accessKeyValue = paramMap.get(ACCESS_KEY);
|
||||
|
||||
// 参数非空校验
|
||||
SaSignException.notEmpty(timestampValue, "缺少 timestamp 字段");
|
||||
SaSignException.notEmpty(nonceValue, "缺少 nonce 字段");
|
||||
SaSignException.notEmpty(signValue, "缺少 sign 字段");
|
||||
SaSignException.notEmpty(appKeyValue, "缺少 appkey 字段");
|
||||
SaSignException.notEmpty(accessKeyValue, "缺少 accessKey 字段");
|
||||
|
||||
// 应用存在性校验
|
||||
SaSignException.notTrue(!appService.isExistAppKey(appKeyValue), "应用不存在");
|
||||
SaSignException.notTrue(!appService.isAppExists(ACCESS_KEY), "应用不存在");
|
||||
|
||||
// 应用是否过期校验
|
||||
SaSignException.notTrue(appService.isExpireAppKey(appKeyValue), "应用已过期");
|
||||
SaSignException.notTrue(appService.isAppSecretExpired(ACCESS_KEY), "应用已过期");
|
||||
|
||||
// 依次校验三个参数
|
||||
checkTimestamp(Long.parseLong(timestampValue));
|
||||
@@ -67,7 +73,7 @@ public class OpenSignTemplate extends SaSignTemplate {
|
||||
public String createSign(Map<String, ?> paramsMap) {
|
||||
// 根据应用密钥获取对应的应用密码
|
||||
String appKey = (String)((Map)paramsMap).get("appkey");
|
||||
String secretKey = this.appService.getAppSecretByAppKey(appKey);
|
||||
String secretKey = this.appService.getSecretKeyByAccessKey(appKey);
|
||||
SaSignException.notEmpty(secretKey, "参与参数签名的秘钥不可为空", SaErrorCode.CODE_12201);
|
||||
|
||||
// 如果调用者不小心传入了 sign 参数,则此处需要将 sign 参数排除在外
|
||||
|
@@ -41,10 +41,9 @@
|
||||
<dependency>
|
||||
<groupId>top.continew</groupId>
|
||||
<artifactId>continew-plugin-schedule</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 能力开放插件(包括应用管理、API开放授权、API开发等) -->
|
||||
<!-- 能力开放插件(后续会改造为独立插件) -->
|
||||
<dependency>
|
||||
<groupId>top.continew</groupId>
|
||||
<artifactId>continew-plugin-open</artifactId>
|
||||
|
@@ -20,27 +20,27 @@ import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.enums.ParameterIn;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import top.continew.admin.open.model.resp.AppSecretGetResp;
|
||||
import top.continew.starter.extension.crud.enums.Api;
|
||||
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import top.continew.starter.extension.crud.annotation.CrudRequestMapping;
|
||||
import top.continew.starter.extension.crud.controller.BaseController;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PatchMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import top.continew.admin.open.model.query.AppQuery;
|
||||
import top.continew.admin.open.model.req.AppReq;
|
||||
import top.continew.admin.open.model.resp.AppDetailResp;
|
||||
import top.continew.admin.open.model.resp.AppResp;
|
||||
import top.continew.admin.open.model.resp.AppSecretResp;
|
||||
import top.continew.admin.open.service.AppService;
|
||||
import top.continew.starter.extension.crud.model.resp.BaseIdResp;
|
||||
import top.continew.starter.extension.crud.annotation.CrudRequestMapping;
|
||||
import top.continew.starter.extension.crud.controller.BaseController;
|
||||
import top.continew.starter.extension.crud.enums.Api;
|
||||
|
||||
/**
|
||||
* 应用管理 API
|
||||
*
|
||||
* @author chengzi
|
||||
* @author Charles7c
|
||||
* @since 2024/10/17 16:03
|
||||
*/
|
||||
@Tag(name = "应用管理 API")
|
||||
@@ -49,31 +49,19 @@ import top.continew.starter.extension.crud.model.resp.BaseIdResp;
|
||||
@CrudRequestMapping(value = "/open/app", api = {Api.PAGE, Api.DETAIL, Api.ADD, Api.UPDATE, Api.DELETE, Api.EXPORT})
|
||||
public class AppController extends BaseController<AppService, AppResp, AppDetailResp, AppQuery, AppReq> {
|
||||
|
||||
private final AppService appService;
|
||||
private final static String APP_DISABLED_KEY = "0";
|
||||
|
||||
@Operation(summary = "刷新应用密码", description = "刷新应用密码")
|
||||
@Parameter(name = "id", description = "ID", example = "test", in = ParameterIn.PATH)
|
||||
@SaCheckPermission("open:app:refreshas")
|
||||
@GetMapping(value = "/{id}/refreshas")
|
||||
public void refreshAppSecret(@PathVariable Long id) {
|
||||
appService.refreshAppSecretByID(id);
|
||||
@Operation(summary = "获取密钥", description = "获取应用密钥")
|
||||
@Parameter(name = "id", description = "ID", example = "1", in = ParameterIn.PATH)
|
||||
@SaCheckPermission("open:app:secret")
|
||||
@GetMapping("/{id}/secret")
|
||||
public AppSecretResp getSecret(@PathVariable Long id) {
|
||||
return baseService.getSecret(id);
|
||||
}
|
||||
|
||||
@Operation(summary = "获取应用密码", description = "获取应用密码")
|
||||
@Parameter(name = "appKey", description = "应用密钥", example = "test", in = ParameterIn.PATH)
|
||||
@SaCheckPermission("open:app:getas")
|
||||
@GetMapping("/{id}/appsecret")
|
||||
public AppSecretGetResp getAppSecret(@PathVariable Long id) {
|
||||
return appService.getAppSecretById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaseIdResp<Long> add(AppReq req) {
|
||||
BaseIdResp<Long> baseIdResp = super.add(req);
|
||||
Long appId = baseIdResp.getId();
|
||||
appService.refreshAppSecretByID(appId);
|
||||
appService.resetAppSecretStatusById(appId, APP_DISABLED_KEY);
|
||||
return baseIdResp;
|
||||
@Operation(summary = "重置密钥", description = "重置应用密钥")
|
||||
@Parameter(name = "id", description = "ID", example = "1", in = ParameterIn.PATH)
|
||||
@SaCheckPermission("open:app:resetSecret")
|
||||
@PatchMapping("/{id}/secret")
|
||||
public void resetSecret(@PathVariable Long id) {
|
||||
baseService.resetSecret(id);
|
||||
}
|
||||
}
|
@@ -7,8 +7,8 @@ databaseChangeLog:
|
||||
file: db/changelog/mysql/main_data.sql
|
||||
- include:
|
||||
file: db/changelog/mysql/plugin/plugin_schedule.sql
|
||||
# - include:
|
||||
# file: db/changelog/mysql/plugin/plugin_open.sql
|
||||
- include:
|
||||
file: db/changelog/mysql/plugin/plugin_open.sql
|
||||
- include:
|
||||
file: db/changelog/mysql/plugin/plugin_generator.sql
|
||||
# PostgreSQL
|
||||
|
@@ -4,19 +4,19 @@
|
||||
-- comment 初始化能力开放插件
|
||||
-- 初始化表结构
|
||||
CREATE TABLE IF NOT EXISTS `sys_app` (
|
||||
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ID',
|
||||
`name` varchar(255) DEFAULT NULL COMMENT '应用名称',
|
||||
`app_key` varchar(255) DEFAULT NULL COMMENT '应用密钥',
|
||||
`app_secret` varchar(255) DEFAULT NULL COMMENT '应用密码',
|
||||
`status` varchar(255) DEFAULT NULL COMMENT '应用状态 (0: 未激活;1: 激活)',
|
||||
`expiration_time` datetime DEFAULT NULL COMMENT '失效时间',
|
||||
`app_desc` varchar(255) DEFAULT NULL COMMENT '应用描述',
|
||||
`secret_status` varchar(255) DEFAULT NULL COMMENT '应用密码查看状态 (0: 未查看;1: 已查看)',
|
||||
`create_user` bigint(20) NOT NULL COMMENT '创建人',
|
||||
`create_time` datetime NOT NULL COMMENT '创建时间',
|
||||
`update_user` bigint(20) DEFAULT NULL COMMENT '修改人',
|
||||
`update_time` datetime DEFAULT NULL COMMENT '修改时间',
|
||||
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ID',
|
||||
`name` varchar(100) NOT NULL COMMENT '名称',
|
||||
`access_key` varchar(255) NOT NULL COMMENT 'Access Key(访问密钥)',
|
||||
`secret_key` varchar(255) NOT NULL COMMENT 'Secret Key(私有密钥)',
|
||||
`expire_time` datetime DEFAULT NULL COMMENT '失效时间',
|
||||
`description` varchar(200) DEFAULT NULL COMMENT '描述',
|
||||
`status` tinyint(1) UNSIGNED NOT NULL DEFAULT 1 COMMENT '状态(1:启用;2:禁用)',
|
||||
`create_user` bigint(20) NOT NULL COMMENT '创建人',
|
||||
`create_time` datetime NOT NULL COMMENT '创建时间',
|
||||
`update_user` bigint(20) DEFAULT NULL COMMENT '修改人',
|
||||
`update_time` datetime DEFAULT NULL COMMENT '修改时间',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE INDEX `uk_access_key`(`access_key`),
|
||||
INDEX `idx_create_user`(`create_user`),
|
||||
INDEX `idx_update_user`(`update_user`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='应用表';
|
||||
@@ -32,5 +32,6 @@ VALUES
|
||||
(7013, '新增', 7010, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'open:app:add', 3, 1, 1, NOW(), NULL, NULL),
|
||||
(7014, '修改', 7010, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'open:app:update', 4, 1, 1, NOW(), NULL, NULL),
|
||||
(7015, '删除', 7010, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'open:app:delete', 5, 1, 1, NOW(), NULL, NULL),
|
||||
(7016, '导出', 7010, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'open:app:export', 6, 1, 1, NOW(), NULL, NULL);
|
||||
|
||||
(7016, '导出', 7010, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'open:app:export', 6, 1, 1, NOW(), NULL, NULL),
|
||||
(7017, '查看密钥', 7010, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'open:app:secret', 7, 1, 1, NOW(), NULL, NULL),
|
||||
(7018, '重置密钥', 7010, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'open:app:resetSecret', 8, 1, 1, NOW(), NULL, NULL);
|
||||
|
@@ -4,30 +4,29 @@
|
||||
-- comment 初始化能力开放插件
|
||||
-- 初始化表结构
|
||||
CREATE TABLE IF NOT EXISTS "sys_app" (
|
||||
"id" int8 DEFAULT NULL,
|
||||
"name" varchar(255) DEFAULT NULL,
|
||||
"app_key" varchar(255) DEFAULT NULL,
|
||||
"app_secret" varchar(255) DEFAULT NULL,
|
||||
"status" varchar(255) DEFAULT NULL,
|
||||
"expiration_time" timestamp DEFAULT NULL,
|
||||
"app_desc" varchar(255) DEFAULT NULL,
|
||||
"secret_status" varchar(255) DEFAULT NULL,
|
||||
"create_user" int8 NOT NULL,
|
||||
"create_time" timestamp NOT NULL,
|
||||
"update_user" int8 NOT NULL,
|
||||
"update_time" timestamp NOT NULL,
|
||||
"id" int8 NOT NULL,
|
||||
"name" varchar(100) NOT NULL,
|
||||
"access_key" varchar(255) NOT NULL,
|
||||
"secret_key" varchar(255) NOT NULL,
|
||||
"expire_time" timestamp DEFAULT NULL,
|
||||
"description" varchar(200) DEFAULT NULL,
|
||||
"status" int2 NOT NULL DEFAULT 1,
|
||||
"create_user" int8 NOT NULL,
|
||||
"create_time" timestamp NOT NULL,
|
||||
"update_user" int8 DEFAULT NULL,
|
||||
"update_time" timestamp DEFAULT NULL,
|
||||
PRIMARY KEY ("id")
|
||||
);
|
||||
CREATE UNIQUE INDEX "uk_app_access_key" ON "sys_app" ("access_key");
|
||||
CREATE INDEX "idx_app_create_user" ON "sys_app" ("create_user");
|
||||
CREATE INDEX "idx_app_update_user" ON "sys_app" ("update_user");
|
||||
COMMENT ON COLUMN "sys_app"."id" IS 'ID';
|
||||
COMMENT ON COLUMN "sys_app"."name" IS '名称';
|
||||
COMMENT ON COLUMN "sys_app"."app_key" IS '应用密钥';
|
||||
COMMENT ON COLUMN "sys_app"."app_secret" IS '应用密码';
|
||||
COMMENT ON COLUMN "sys_app"."status" IS '应用状态 (0: 未激活;1: 激活)';
|
||||
COMMENT ON COLUMN "sys_app"."expiration_time" IS '失效时间';
|
||||
COMMENT ON COLUMN "sys_app"."app_desc" IS '应用描述';
|
||||
COMMENT ON COLUMN "sys_app"."secret_status" IS '应用密码查看状态 (0: 未查看;1: 已查看)';
|
||||
COMMENT ON COLUMN "sys_app"."access_key" IS 'Access Key(访问密钥)';
|
||||
COMMENT ON COLUMN "sys_app"."secret_key" IS 'Secret Key(私有密钥)';
|
||||
COMMENT ON COLUMN "sys_app"."expire_time" IS '失效时间';
|
||||
COMMENT ON COLUMN "sys_app"."description" IS '描述';
|
||||
COMMENT ON COLUMN "sys_app"."status" IS '状态(1:启用;2:禁用)';
|
||||
COMMENT ON COLUMN "sys_app"."create_user" IS '创建人';
|
||||
COMMENT ON COLUMN "sys_app"."create_time" IS '创建时间';
|
||||
COMMENT ON COLUMN "sys_app"."update_user" IS '修改人';
|
||||
@@ -45,5 +44,6 @@ VALUES
|
||||
(7013, '新增', 7010, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'open:app:add', 3, 1, 1, NOW(), NULL, NULL),
|
||||
(7014, '修改', 7010, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'open:app:update', 4, 1, 1, NOW(), NULL, NULL),
|
||||
(7015, '删除', 7010, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'open:app:delete', 5, 1, 1, NOW(), NULL, NULL),
|
||||
(7016, '导出', 7010, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'open:app:export', 6, 1, 1, NOW(), NULL, NULL);
|
||||
|
||||
(7016, '导出', 7010, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'open:app:export', 6, 1, 1, NOW(), NULL, NULL),
|
||||
(7017, '查看密钥', 7010, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'open:app:secret', 7, 1, 1, NOW(), NULL, NULL),
|
||||
(7018, '重置密钥', 7010, 3, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'open:app:resetSecret', 8, 1, 1, NOW(), NULL, NULL);
|
||||
|
Reference in New Issue
Block a user