refactor: 网关消除魔法值,scope/事件/请求头/MCP 协议等改为枚举与常量

- 新增 ScopeMatcher、GatewayAuthorities、JwtClaims、GatewayHeaders、InternalPaths、McpProtocol/McpToolArgs 等常量与枚举
- Webhook 消息体改用 ObjectMapper 构建,修复原先仅转义引号的问题
- 管理接口与 OpenAPI 控制器的默认值、响应字段、演示数据集中到 AdminDefaults / OpenApiFields

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
wjh
2026-10-10 16:27:53 +08:00
co-authored by Claude Sonnet 5
parent 33269da935
commit 761636c2c5
45 changed files with 1557 additions and 654 deletions
@@ -18,6 +18,17 @@ public class IpUtils {
private static final String UNKNOWN = "unknown";
private static final String LOCALHOST_IPV4 = "127.0.0.1";
private static final String LOCALHOST_IPV6 = "0:0:0:0:0:0:0:1";
private static final String WILDCARD = "*";
private static final String ANY_CIDR = "0.0.0.0/0";
private static final String CIDR_SEPARATOR = "/";
private static final String IP_LIST_SEPARATOR = ",";
/** 代理配置的分隔符:逗号、分号、空白 */
private static final String PROXY_CONFIG_SPLIT_REGEX = "[,;\\s]+";
/** 白名单配置的分隔符:逗号、分号、中文逗号、换行 */
private static final String WHITELIST_SPLIT_REGEX = "[,;,\\n\\r]+";
private static final int BITS_PER_BYTE = 8;
private static final int BYTE_MASK = 0xFF;
private static final int CIDR_PART_COUNT = 2;
/**
* 默认信任的内网/本地反向代理 IP 网段 (如本地 Nginx / 宿主机代理)
@@ -34,7 +45,7 @@ public class IpUtils {
public static void setTrustedProxies(String proxiesConfig) {
if (StringUtils.hasText(proxiesConfig)) {
TRUSTED_PROXIES.clear();
for (String p : proxiesConfig.split("[,;\\s]+")) {
for (String p : proxiesConfig.split(PROXY_CONFIG_SPLIT_REGEX)) {
if (StringUtils.hasText(p)) {
TRUSTED_PROXIES.add(p.trim());
}
@@ -61,23 +72,23 @@ public class IpUtils {
}
// 仅在确认对端为受信任反向代理的前提下,提取代理传递的源 IP
String ip = request.getHeader("X-Forwarded-For");
String ip = request.getHeader(GatewayHeaders.FORWARDED_FOR);
if (isInvalidIp(ip)) {
ip = request.getHeader("X-Real-IP");
ip = request.getHeader(GatewayHeaders.REAL_IP);
}
if (isInvalidIp(ip)) {
ip = request.getHeader("Proxy-Client-IP");
ip = request.getHeader(GatewayHeaders.PROXY_CLIENT_IP);
}
if (isInvalidIp(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
ip = request.getHeader(GatewayHeaders.WL_PROXY_CLIENT_IP);
}
if (isInvalidIp(ip)) {
ip = remoteAddr;
}
// 多级反向代理会有多个逗号分隔的 IP,首个为客户端真实源 IP
if (StringUtils.hasText(ip) && ip.contains(",")) {
for (String subIp : ip.split(",")) {
if (StringUtils.hasText(ip) && ip.contains(IP_LIST_SEPARATOR)) {
for (String subIp : ip.split(IP_LIST_SEPARATOR)) {
String trimmed = subIp.trim();
if (!isInvalidIp(trimmed)) {
ip = trimmed;
@@ -123,7 +134,7 @@ public class IpUtils {
String normalizedClientIp = LOCALHOST_IPV6.equals(clientIp.trim()) ? LOCALHOST_IPV4 : clientIp.trim();
String[] rules = whitelist.split("[,;,\\n\\r]+");
String[] rules = whitelist.split(WHITELIST_SPLIT_REGEX);
for (String rule : rules) {
String pattern = rule.trim();
if (!pattern.isEmpty() && matchIp(normalizedClientIp, pattern)) {
@@ -148,15 +159,15 @@ public class IpUtils {
if (LOCALHOST_IPV6.equals(ip)) ip = LOCALHOST_IPV4;
if (LOCALHOST_IPV6.equals(pattern)) pattern = LOCALHOST_IPV4;
if ("*".equals(pattern) || "0.0.0.0/0".equals(pattern)) {
if (WILDCARD.equals(pattern) || ANY_CIDR.equals(pattern)) {
return true;
}
if (pattern.contains("/")) {
if (pattern.contains(CIDR_SEPARATOR)) {
return matchCidr(ip, pattern);
}
if (pattern.contains("*")) {
if (pattern.contains(WILDCARD)) {
return matchWildcard(ip, pattern);
}
@@ -164,14 +175,14 @@ public class IpUtils {
}
private static boolean matchWildcard(String ip, String pattern) {
String regex = pattern.replace(".", "\\.").replace("*", ".*");
String regex = pattern.replace(".", "\\.").replace(WILDCARD, ".*");
return ip.matches(regex);
}
private static boolean matchCidr(String ip, String cidr) {
try {
String[] parts = cidr.split("/");
if (parts.length != 2) return false;
String[] parts = cidr.split(CIDR_SEPARATOR);
if (parts.length != CIDR_PART_COUNT) return false;
String network = parts[0];
int prefixLength = Integer.parseInt(parts[1]);
@@ -181,15 +192,15 @@ public class IpUtils {
if (ipBytes.length != netBytes.length) return false;
int byteCount = prefixLength / 8;
int bitCount = prefixLength % 8;
int byteCount = prefixLength / BITS_PER_BYTE;
int bitCount = prefixLength % BITS_PER_BYTE;
for (int i = 0; i < byteCount; i++) {
if (ipBytes[i] != netBytes[i]) return false;
}
if (bitCount > 0 && byteCount < ipBytes.length) {
int mask = (0xFF << (8 - bitCount)) & 0xFF;
int mask = (BYTE_MASK << (BITS_PER_BYTE - bitCount)) & BYTE_MASK;
return (ipBytes[byteCount] & mask) == (netBytes[byteCount] & mask);
}