秘钥加密

This commit is contained in:
LiJinHui
2025-09-26 10:04:43 +08:00
parent d022dd385a
commit a8932f3743
3 changed files with 240 additions and 98 deletions
@@ -0,0 +1,96 @@
package xiaozhi.common.utils;
import cn.hutool.json.JSONObject;
import org.apache.commons.lang3.StringUtils;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
/**
* 敏感数据处理工具类
*/
public class SensitiveDataUtils {
// 敏感字段列表
private static final Set<String> SENSITIVE_FIELDS = new HashSet<>(Arrays.asList(
"api_key", "personal_access_token", "access_token", "token",
"secret", "access_key_secret", "secret_key"
));
/**
* 隐藏字符串中间部分
* @param value 原始字符串
* @return 隐藏后的字符串
*/
public static String maskMiddle(String value) {
if (StringUtils.isBlank(value)) {
return value;
}
int length = value.length();
if (length <= 8) {
// 字符串太短,返回前2后2,中间用*代替
return value.substring(0, 2) + "****" + value.substring(length - 2);
} else {
// 返回前4后4,中间用*代替
int maskLength = length - 8;
StringBuilder maskBuilder = new StringBuilder();
for (int i = 0; i < maskLength; i++) {
maskBuilder.append('*');
}
return value.substring(0, 4) + maskBuilder.toString() + value.substring(length - 4);
}
}
/**
* 处理JSONObject中的敏感字段
* @param jsonObject 原始JSONObject
* @return 处理后的JSONObject副本
*/
public static JSONObject maskSensitiveFields(JSONObject jsonObject) {
if (jsonObject == null) {
return null;
}
// 创建副本避免修改原始数据
JSONObject result = new JSONObject();
for (String key : jsonObject.keySet()) {
Object value = jsonObject.get(key);
if (SENSITIVE_FIELDS.contains(key.toLowerCase()) && value instanceof String) {
// 处理敏感字段
result.put(key, maskMiddle((String) value));
} else if (value instanceof JSONObject) {
// 递归处理嵌套的JSONObject
result.put(key, maskSensitiveFields((JSONObject) value));
} else {
// 非敏感字段保持不变
result.put(key, value);
}
}
return result;
}
/**
* 比较两个JSONObject的敏感字段处理后是否相同
* @param original 原始JSONObject
* @param updated 更新后的JSONObject
* @return 是否相同
*/
public static boolean isSensitiveDataEqual(JSONObject original, JSONObject updated) {
if (original == null && updated == null) {
return true;
}
if (original == null || updated == null) {
return false;
}
JSONObject maskedOriginal = maskSensitiveFields(original);
JSONObject maskedUpdated = maskSensitiveFields(updated);
return maskedOriginal.toString().equals(maskedUpdated.toString());
}
}
@@ -11,6 +11,7 @@ import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import cn.hutool.json.JSONObject;
import lombok.AllArgsConstructor;
import xiaozhi.common.constant.Constant;
import xiaozhi.common.exception.ErrorCode;
@@ -19,6 +20,7 @@ import xiaozhi.common.redis.RedisKeys;
import xiaozhi.common.redis.RedisUtils;
import xiaozhi.common.utils.ConvertUtils;
import xiaozhi.common.utils.JsonUtils;
import xiaozhi.common.utils.SensitiveDataUtils;
import xiaozhi.modules.agent.dao.AgentVoicePrintDao;
import xiaozhi.modules.agent.entity.AgentEntity;
import xiaozhi.modules.agent.entity.AgentPluginMapping;
@@ -349,6 +351,9 @@ public class ConfigServiceImpl implements ConfigService {
* @param intentModelId 意图模型ID
* @param result 结果Map
*/
/**
* 构建模块配置
*/
private void buildModuleConfig(
String assistantName,
String prompt,
@@ -366,12 +371,12 @@ public class ConfigServiceImpl implements ConfigService {
Map<String, Object> result,
boolean isCache) {
Map<String, String> selectedModule = new HashMap<>();
String[] modelTypes = { "VAD", "ASR", "TTS", "Memory", "Intent", "LLM", "VLLM" };
String[] modelIds = { vadModelId, asrModelId, ttsModelId, memModelId, intentModelId, llmModelId, vllmModelId };
String intentLLMModelId = null;
String memLocalShortLLMModelId = null;
for (int i = 0; i < modelIds.length; i++) {
if (modelIds[i] == null) {
continue;
@@ -382,66 +387,20 @@ public class ConfigServiceImpl implements ConfigService {
}
Map<String, Object> typeConfig = new HashMap<>();
if (model.getConfigJson() != null) {
typeConfig.put(model.getId(), model.getConfigJson());
// 如果是TTS类型,添加private_voice属性
if ("TTS".equals(modelTypes[i])) {
if (voice != null)
((Map<String, Object>) model.getConfigJson()).put("private_voice", voice);
if (referenceAudio != null)
((Map<String, Object>) model.getConfigJson()).put("ref_audio", referenceAudio);
if (referenceText != null)
((Map<String, Object>) model.getConfigJson()).put("ref_text", referenceText);
}
// 如果是Intent类型,且type=intent_llm,则给他添加附加模型
if ("Intent".equals(modelTypes[i])) {
Map<String, Object> map = (Map<String, Object>) model.getConfigJson();
if ("intent_llm".equals(map.get("type"))) {
intentLLMModelId = (String) map.get("llm");
if (StringUtils.isNotBlank(intentLLMModelId) && intentLLMModelId.equals(llmModelId)) {
intentLLMModelId = null;
}
}
if (map.get("functions") != null) {
String functionStr = (String) map.get("functions");
if (StringUtils.isNotBlank(functionStr)) {
String[] functions = functionStr.split("\\;");
map.put("functions", functions);
}
}
System.out.println("map: " + map);
}
if ("Memory".equals(modelTypes[i])) {
Map<String, Object> map = (Map<String, Object>) model.getConfigJson();
if ("mem_local_short".equals(map.get("type"))) {
memLocalShortLLMModelId = (String) map.get("llm");
if (StringUtils.isNotBlank(memLocalShortLLMModelId)
&& memLocalShortLLMModelId.equals(llmModelId)) {
memLocalShortLLMModelId = null;
}
}
}
// 如果是LLM类型,且intentLLMModelId不为空,则添加附加模型
if ("LLM".equals(modelTypes[i])) {
if (StringUtils.isNotBlank(intentLLMModelId)) {
if (!typeConfig.containsKey(intentLLMModelId)) {
ModelConfigEntity intentLLM = modelConfigService.getModelById(intentLLMModelId, isCache);
typeConfig.put(intentLLM.getId(), intentLLM.getConfigJson());
}
}
if (StringUtils.isNotBlank(memLocalShortLLMModelId)) {
if (!typeConfig.containsKey(memLocalShortLLMModelId)) {
ModelConfigEntity memLocalShortLLM = modelConfigService
.getModelById(memLocalShortLLMModelId, isCache);
typeConfig.put(memLocalShortLLM.getId(), memLocalShortLLM.getConfigJson());
}
}
}
// 复制一份配置,避免修改原始数据
JSONObject configJsonCopy = new JSONObject(model.getConfigJson());
// 对敏感数据进行隐藏处理
JSONObject maskedConfigJson = SensitiveDataUtils.maskSensitiveFields(configJsonCopy);
typeConfig.put(model.getId(), maskedConfigJson);
}
result.put(modelTypes[i], typeConfig);
selectedModule.put(modelTypes[i], model.getId());
}
result.put("selected_module", selectedModule);
if (StringUtils.isNotBlank(prompt)) {
prompt = prompt.replace("{{assistant_name}}", StringUtils.isBlank(assistantName) ? "小智" : assistantName);
@@ -15,6 +15,7 @@ import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.json.JSONObject;
import lombok.AllArgsConstructor;
import xiaozhi.common.constant.Constant;
import xiaozhi.common.exception.ErrorCode;
@@ -24,6 +25,7 @@ import xiaozhi.common.redis.RedisKeys;
import xiaozhi.common.redis.RedisUtils;
import xiaozhi.common.service.impl.BaseServiceImpl;
import xiaozhi.common.utils.ConvertUtils;
import xiaozhi.common.utils.SensitiveDataUtils;
import xiaozhi.modules.agent.dao.AgentDao;
import xiaozhi.modules.agent.entity.AgentEntity;
import xiaozhi.modules.model.dao.ModelConfigDao;
@@ -36,6 +38,8 @@ import xiaozhi.modules.model.entity.ModelConfigEntity;
import xiaozhi.modules.model.service.ModelConfigService;
import xiaozhi.modules.model.service.ModelProviderService;
import java.io.Serializable;
@Service
@AllArgsConstructor
public class ModelConfigServiceImpl extends BaseServiceImpl<ModelConfigDao, ModelConfigEntity>
@@ -101,35 +105,24 @@ public class ModelConfigServiceImpl extends BaseServiceImpl<ModelConfigDao, Mode
return getPageData(modelConfigEntityIPage, ModelConfigDTO.class);
}
@Override
public ModelConfigDTO add(String modelType, String provideCode, ModelConfigBodyDTO modelConfigBodyDTO) {
// 先验证有没有供应器
if (StringUtils.isBlank(modelType) || StringUtils.isBlank(provideCode)) {
throw new RenException(ErrorCode.MODEL_TYPE_PROVIDE_CODE_NOT_NULL);
}
List<ModelProviderDTO> providerList = modelProviderService.getList(modelType, provideCode);
if (CollectionUtil.isEmpty(providerList)) {
throw new RenException(ErrorCode.MODEL_PROVIDER_NOT_EXIST);
}
// 再保存供应器提供的模型
ModelConfigEntity modelConfigEntity = ConvertUtils.sourceToTarget(modelConfigBodyDTO, ModelConfigEntity.class);
modelConfigEntity.setModelType(modelType);
modelConfigEntity.setIsDefault(0);
modelConfigDao.insert(modelConfigEntity);
return ConvertUtils.sourceToTarget(modelConfigEntity, ModelConfigDTO.class);
}
@Override
public ModelConfigDTO edit(String modelType, String provideCode, String id, ModelConfigBodyDTO modelConfigBodyDTO) {
// 先验证有没有供应器
if (StringUtils.isBlank(modelType) || StringUtils.isBlank(provideCode)) {
throw new RenException(ErrorCode.MODEL_TYPE_PROVIDE_CODE_NOT_NULL);
throw new RenException("modelType和provideCode不能为空");
}
List<ModelProviderDTO> providerList = modelProviderService.getList(modelType, provideCode);
if (CollectionUtil.isEmpty(providerList)) {
throw new RenException(ErrorCode.MODEL_PROVIDER_NOT_EXIST);
}
// 获取原始配置
ModelConfigEntity originalEntity = modelConfigDao.selectById(id);
if (originalEntity == null) {
throw new RenException(ErrorCode.RESOURCE_NOT_FOUND);
}
// 验证LLM配置
if (modelConfigBodyDTO.getConfigJson().containsKey("llm")) {
String llm = modelConfigBodyDTO.getConfigJson().get("llm").toString();
ModelConfigEntity modelConfigEntity = modelConfigDao.selectOne(new LambdaQueryWrapper<ModelConfigEntity>()
@@ -145,15 +138,63 @@ public class ModelConfigServiceImpl extends BaseServiceImpl<ModelConfigDao, Mode
throw new RenException(ErrorCode.INVALID_LLM_TYPE);
}
}
// 再更新供应器提供的模型
ModelConfigEntity modelConfigEntity = ConvertUtils.sourceToTarget(modelConfigBodyDTO, ModelConfigEntity.class);
modelConfigEntity.setId(id);
modelConfigEntity.setModelType(modelType);
// 检查敏感字段是否有变化
if (originalEntity.getConfigJson() != null && modelConfigBodyDTO.getConfigJson() != null) {
boolean sensitiveEqual = SensitiveDataUtils.isSensitiveDataEqual(
originalEntity.getConfigJson(),
modelConfigBodyDTO.getConfigJson()
);
if (sensitiveEqual) {
// 敏感数据没有变化,使用原始的configJson值
modelConfigEntity.setConfigJson(originalEntity.getConfigJson());
}
}
modelConfigDao.updateById(modelConfigEntity);
// 清除缓存
redisUtils.delete(RedisKeys.getModelConfigById(modelConfigEntity.getId()));
return ConvertUtils.sourceToTarget(modelConfigEntity, ModelConfigDTO.class);
// 返回数据前处理敏感字段
ModelConfigDTO dto = ConvertUtils.sourceToTarget(modelConfigEntity, ModelConfigDTO.class);
if (dto.getConfigJson() != null) {
dto.setConfigJson(SensitiveDataUtils.maskSensitiveFields(dto.getConfigJson()));
}
return dto;
}
@Override
public ModelConfigDTO add(String modelType, String provideCode, ModelConfigBodyDTO modelConfigBodyDTO) {
// 先验证有没有供应器
if (StringUtils.isBlank(modelType) || StringUtils.isBlank(provideCode)) {
throw new RenException("modelType和provideCode不能为空");
}
List<ModelProviderDTO> providerList = modelProviderService.getList(modelType, provideCode);
if (CollectionUtil.isEmpty(providerList)) {
throw new RenException(ErrorCode.MODEL_PROVIDER_NOT_EXIST);
}
// 保存供应器提供的模型
ModelConfigEntity modelConfigEntity = ConvertUtils.sourceToTarget(modelConfigBodyDTO, ModelConfigEntity.class);
modelConfigEntity.setModelType(modelType);
modelConfigEntity.setIsDefault(0);
modelConfigDao.insert(modelConfigEntity);
// 返回数据前处理敏感字段
ModelConfigDTO dto = ConvertUtils.sourceToTarget(modelConfigEntity, ModelConfigDTO.class);
if (dto.getConfigJson() != null) {
dto.setConfigJson(SensitiveDataUtils.maskSensitiveFields(dto.getConfigJson()));
}
return dto;
}
@Override
@@ -242,24 +283,6 @@ public class ModelConfigServiceImpl extends BaseServiceImpl<ModelConfigDao, Mode
return null;
}
@Override
public ModelConfigEntity getModelById(String id, boolean isCache) {
if (StringUtils.isBlank(id)) {
return null;
}
if (isCache) {
ModelConfigEntity cachedConfig = (ModelConfigEntity) redisUtils.get(RedisKeys.getModelConfigById(id));
if (cachedConfig != null) {
return ConvertUtils.sourceToTarget(cachedConfig, ModelConfigEntity.class);
}
}
ModelConfigEntity entity = modelConfigDao.selectById(id);
if (entity != null) {
redisUtils.set(RedisKeys.getModelConfigById(id), entity);
}
return entity;
}
@Override
public void setDefaultModel(String modelType, int isDefault) {
ModelConfigEntity entity = new ModelConfigEntity();
@@ -267,4 +290,68 @@ public class ModelConfigServiceImpl extends BaseServiceImpl<ModelConfigDao, Mode
modelConfigDao.update(entity, new QueryWrapper<ModelConfigEntity>()
.eq("model_type", modelType));
}
@Override
public ModelConfigEntity selectById(Serializable id) {
ModelConfigEntity entity = super.selectById(id);
if (entity != null && entity.getConfigJson() != null) {
// 对配置中的敏感数据进行隐藏处理
JSONObject maskedConfigJson = SensitiveDataUtils.maskSensitiveFields(entity.getConfigJson());
entity.setConfigJson(maskedConfigJson);
}
return entity;
}
// 重写getPageData方法,添加敏感数据处理
@Override
protected <D> PageData<D> getPageData(IPage<?> page, Class<D> target) {
List<?> records = page.getRecords();
if (records != null && !records.isEmpty()) {
for (Object record : records) {
if (record instanceof ModelConfigEntity) {
ModelConfigEntity entity = (ModelConfigEntity) record;
if (entity.getConfigJson() != null) {
// 对配置中的敏感数据进行隐藏处理
JSONObject maskedConfigJson = SensitiveDataUtils.maskSensitiveFields(entity.getConfigJson());
entity.setConfigJson(maskedConfigJson);
}
}
}
}
return super.getPageData(page, target);
}
// 确保只有一个getModelById方法实现
@Override
public ModelConfigEntity getModelById(String id, boolean isCache) {
ModelConfigEntity entity = null;
if (isCache) {
String cacheKey = RedisKeys.getModelConfigById(id);
entity = (ModelConfigEntity) redisUtils.get(cacheKey);
if (entity != null) {
// 从缓存获取的数据也需要处理敏感信息
if (entity.getConfigJson() != null) {
JSONObject maskedConfigJson = SensitiveDataUtils.maskSensitiveFields(entity.getConfigJson());
entity.setConfigJson(maskedConfigJson);
}
return entity;
}
}
// 从数据库获取数据
entity = modelConfigDao.selectById(id);
if (entity != null) {
// 处理敏感信息
if (entity.getConfigJson() != null) {
JSONObject maskedConfigJson = SensitiveDataUtils.maskSensitiveFields(entity.getConfigJson());
entity.setConfigJson(maskedConfigJson);
}
if (isCache) {
// 缓存处理后的对象
redisUtils.set(RedisKeys.getModelConfigById(id), entity);
}
}
return entity;
}
}