mirror of
https://codeup.aliyun.com/67c68d4e484ca2f0a13ac3c1/ydc/jsowell-charger-web.git
synced 2026-07-23 07:31:51 +08:00
增强待办路由安全和查询索引
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
package com.jsowell.system.service.impl;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.jsowell.common.exception.ServiceException;
|
||||
import com.jsowell.common.util.StringUtils;
|
||||
import com.jsowell.system.constant.TodoTaskConstants;
|
||||
@@ -9,11 +11,17 @@ import com.jsowell.system.domain.dto.TodoTaskQuery;
|
||||
import com.jsowell.system.mapper.SysTodoTaskMapper;
|
||||
import com.jsowell.system.service.TodoTaskService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.dao.DuplicateKeyException;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 统一待办任务服务实现。
|
||||
@@ -23,6 +31,12 @@ import java.util.List;
|
||||
@Service
|
||||
public class TodoTaskServiceImpl implements TodoTaskService {
|
||||
private static final String SYSTEM_OPERATOR = "system";
|
||||
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
|
||||
private static final Set<String> DEFAULT_ROUTE_WHITELIST = new HashSet<>(
|
||||
Arrays.asList("invoiceDetail", "financeDetail"));
|
||||
|
||||
@Value("${todo.route-whitelist:invoiceDetail,financeDetail}")
|
||||
private String routeWhitelist;
|
||||
|
||||
@Autowired
|
||||
private SysTodoTaskMapper todoTaskMapper;
|
||||
@@ -223,8 +237,10 @@ public class TodoTaskServiceImpl implements TodoTaskService {
|
||||
todoTask.setContent(normalizeOptional(command.getContent(), "待办内容", 20000));
|
||||
todoTask.setBusinessType(normalizeRequired(command.getBusinessType(), "业务类型", 64));
|
||||
todoTask.setBusinessId(normalizeRequired(command.getBusinessId(), "业务主键", 64));
|
||||
todoTask.setRouteName(normalizeRequired(command.getRouteName(), "业务路由", 100));
|
||||
todoTask.setRouteParams(normalizeOptional(command.getRouteParams(), "路由参数", 1000));
|
||||
String routeName = normalizeRequired(command.getRouteName(), "业务路由", 100);
|
||||
validateRouteName(routeName);
|
||||
todoTask.setRouteName(routeName);
|
||||
todoTask.setRouteParams(normalizeRouteParams(command.getRouteParams()));
|
||||
todoTask.setAssigneeUserId(command.getAssigneeUserId());
|
||||
todoTask.setAssigneeMerchantId(normalizeOptional(command.getAssigneeMerchantId(), "运营商 ID", 64));
|
||||
todoTask.setPriority(priority);
|
||||
@@ -288,6 +304,83 @@ public class TodoTaskServiceImpl implements TodoTaskService {
|
||||
return safeValue;
|
||||
}
|
||||
|
||||
private void validateRouteName(String routeName) {
|
||||
Set<String> allowedRoutes = DEFAULT_ROUTE_WHITELIST;
|
||||
if (StringUtils.isNotBlank(routeWhitelist)) {
|
||||
allowedRoutes = new HashSet<>();
|
||||
for (String configuredRoute : routeWhitelist.split(",")) {
|
||||
if (StringUtils.isNotBlank(configuredRoute)) {
|
||||
allowedRoutes.add(configuredRoute.trim());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!allowedRoutes.contains(routeName)) {
|
||||
throw new ServiceException("待办业务路由未加入白名单");
|
||||
}
|
||||
}
|
||||
|
||||
private String normalizeRouteParams(String routeParams) {
|
||||
String safeRouteParams = normalizeOptional(routeParams, "路由参数", 1000);
|
||||
if (safeRouteParams == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
JsonNode root = OBJECT_MAPPER.readTree(safeRouteParams);
|
||||
if (root == null || !root.isObject()) {
|
||||
throw new ServiceException("待办路由参数必须是 JSON 对象");
|
||||
}
|
||||
boolean structured = root.has("params") || root.has("query");
|
||||
Iterator<Map.Entry<String, JsonNode>> fields = root.fields();
|
||||
while (fields.hasNext()) {
|
||||
Map.Entry<String, JsonNode> field = fields.next();
|
||||
validateRouteParamKey(field.getKey());
|
||||
if (structured) {
|
||||
if (!"params".equals(field.getKey()) && !"query".equals(field.getKey())) {
|
||||
throw new ServiceException("待办路由参数仅允许 params 和 query");
|
||||
}
|
||||
validateRouteParamObject(field.getValue());
|
||||
} else {
|
||||
validateRouteParamValue(field.getValue());
|
||||
}
|
||||
}
|
||||
return safeRouteParams;
|
||||
} catch (ServiceException exception) {
|
||||
throw exception;
|
||||
} catch (Exception exception) {
|
||||
throw new ServiceException("待办路由参数不是合法 JSON");
|
||||
}
|
||||
}
|
||||
|
||||
private void validateRouteParamObject(JsonNode node) {
|
||||
if (node == null || !node.isObject()) {
|
||||
throw new ServiceException("待办路由 params 和 query 必须是 JSON 对象");
|
||||
}
|
||||
Iterator<Map.Entry<String, JsonNode>> fields = node.fields();
|
||||
while (fields.hasNext()) {
|
||||
Map.Entry<String, JsonNode> field = fields.next();
|
||||
validateRouteParamKey(field.getKey());
|
||||
validateRouteParamValue(field.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
private void validateRouteParamKey(String key) {
|
||||
if (StringUtils.isBlank(key) || key.length() > 64) {
|
||||
throw new ServiceException("待办路由参数名称不合法");
|
||||
}
|
||||
}
|
||||
|
||||
private void validateRouteParamValue(JsonNode value) {
|
||||
if (value == null || value.isNull()) {
|
||||
return;
|
||||
}
|
||||
if (!value.isTextual() && !value.isNumber() && !value.isBoolean()) {
|
||||
throw new ServiceException("待办路由参数值只能是字符串、数字或布尔值");
|
||||
}
|
||||
if (value.isTextual() && value.asText().length() > 500) {
|
||||
throw new ServiceException("待办路由参数值不能超过500个字符");
|
||||
}
|
||||
}
|
||||
|
||||
private String normalizeOperator(String operatorName, Long operatorId) {
|
||||
String safeOperator = StringUtils.isNotEmpty(operatorName)
|
||||
? operatorName.trim()
|
||||
|
||||
Reference in New Issue
Block a user