diff --git a/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/framework/integration/gateway/security/GatewaySecurityFilter.java b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/framework/integration/gateway/security/GatewaySecurityFilter.java index c7b76152..0c9ad557 100644 --- a/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/framework/integration/gateway/security/GatewaySecurityFilter.java +++ b/zt-module-databus/zt-module-databus-server/src/main/java/com/zt/plat/module/databus/framework/integration/gateway/security/GatewaySecurityFilter.java @@ -59,7 +59,8 @@ public class GatewaySecurityFilter extends OncePerRequestFilter { private final ObjectMapper objectMapper; private final ApiGatewayAccessLogger accessLogger; private final AntPathMatcher pathMatcher = new AntPathMatcher(); - private static final TypeReference> MAP_TYPE = new TypeReference<>() {}; + private static final TypeReference> MAP_TYPE = new TypeReference<>() { + }; @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) @@ -68,32 +69,38 @@ public class GatewaySecurityFilter extends OncePerRequestFilter { // 仅处理配置的 API 门户路径,不符合的请求直接放行 boolean matchesPortalPath = properties.getAllBasePaths() .stream() - .map(this::normalizeBasePath) .anyMatch(basePath -> pathMatcher.match(basePath + "/**", pathWithinApplication)); if (!matchesPortalPath) { filterChain.doFilter(request, response); return; } - Long accessLogId = accessLogger.logEntrance(request); - // 校验访问 IP 是否落在允许范围内 - if (!isIpAllowed(request)) { - log.warn("[API-PORTAL] 拦截来自 IP {} 访问 {} 的请求", request.getRemoteAddr(), pathWithinApplication); - response.sendError(HttpStatus.FORBIDDEN.value(), "IP 禁止访问"); - accessLogger.finalizeEarly(request, HttpStatus.FORBIDDEN.value(), "IP 禁止访问"); - return; - } + Long accessLogId = null; ApiGatewayProperties.Security security = properties.getSecurity(); ApiClientCredentialDO credential = null; - if (!security.isEnabled()) { - byte[] originalBody = StreamUtils.copyToByteArray(request.getInputStream()); - CachedBodyHttpServletRequest passthroughRequest = new CachedBodyHttpServletRequest(request, originalBody); - ApiGatewayAccessLogger.propagateLogIdHeader(passthroughRequest, accessLogId); - filterChain.doFilter(passthroughRequest, response); - return; - } + Long tenantId = null; boolean dispatchedToGateway = false; try { - Long tenantId = resolveTenantId(request); + tenantId = resolveTenantId(request); + if (tenantId != null) { + // 绑定租户上下文,保证访问日志与后续数据库操作可写入 tenantId + TenantContextHolder.setTenantId(tenantId); + } + if (!isIpAllowed(request)) { + log.warn("[API-PORTAL] 拦截来自 IP {} 访问 {} 的请求", request.getRemoteAddr(), pathWithinApplication); + accessLogId = accessLogger.logEntrance(request); + response.sendError(HttpStatus.FORBIDDEN.value(), "IP 禁止访问"); + accessLogger.finalizeEarly(request, HttpStatus.FORBIDDEN.value(), "IP 禁止访问"); + return; + } + // IP 校验通过后再补录入口日志,避免无租户信息写库 + accessLogId = accessLogger.logEntrance(request); + if (!security.isEnabled()) { + byte[] originalBody = StreamUtils.copyToByteArray(request.getInputStream()); + CachedBodyHttpServletRequest passthroughRequest = new CachedBodyHttpServletRequest(request, originalBody); + ApiGatewayAccessLogger.propagateLogIdHeader(passthroughRequest, accessLogId); + filterChain.doFilter(passthroughRequest, response); + return; + } // 从请求头解析 appId 并加载客户端凭证,包含匿名访问配置 String appId = requireHeader(request, APP_ID_HEADER, "缺少应用标识"); credential = credentialService.findActiveCredential(appId) @@ -144,17 +151,27 @@ public class GatewaySecurityFilter extends OncePerRequestFilter { responseWrapper.copyBodyToResponse(); } } catch (SecurityValidationException ex) { + if (accessLogId == null) { + accessLogId = accessLogger.logEntrance(request); + } log.warn("[API-PORTAL] 安全校验失败: {}", ex.getMessage()); writeErrorResponse(response, security, credential, ex.status(), ex.getMessage()); if (!dispatchedToGateway) { accessLogger.finalizeEarly(request, ex.status().value(), ex.getMessage()); } } catch (Exception ex) { + if (accessLogId == null) { + accessLogId = accessLogger.logEntrance(request); + } log.error("[API-PORTAL] 处理安全校验时出现异常", ex); writeErrorResponse(response, security, credential, HttpStatus.INTERNAL_SERVER_ERROR, "网关安全校验失败"); if (!dispatchedToGateway) { accessLogger.finalizeEarly(request, HttpStatus.INTERNAL_SERVER_ERROR.value(), "网关安全校验失败"); } + } finally { + if (tenantId != null) { + TenantContextHolder.clear(); + } } } @@ -177,15 +194,6 @@ public class GatewaySecurityFilter extends OncePerRequestFilter { return requestUri; } - private String normalizeBasePath(String basePath) { - String candidate = StringUtils.hasText(basePath) ? basePath : ApiGatewayProperties.DEFAULT_BASE_PATH; - candidate = candidate.startsWith("/") ? candidate : "/" + candidate; - if (candidate.endsWith("/")) { - candidate = candidate.substring(0, candidate.length() - 1); - } - return candidate; - } - private Long resolveTenantId(HttpServletRequest request) { if (!properties.isEnableTenantHeader()) { return null; @@ -306,7 +314,6 @@ public class GatewaySecurityFilter extends OncePerRequestFilter { }); } catch (IllegalArgumentException ex) { log.debug("[API-PORTAL] 解析查询串 {} 失败", queryString, ex); - target.put("query", queryString); } } @@ -321,7 +328,7 @@ public class GatewaySecurityFilter extends OncePerRequestFilter { if (bodyText.startsWith("{")) { try { Map bodyMap = objectMapper.readValue(bodyText, MAP_TYPE); - bodyMap.forEach((key, value) -> target.put(key, normalizeValue(value))); + target.putAll(bodyMap); return; } catch (JsonProcessingException ex) { log.debug("[API-PORTAL] 解析请求体 JSON 失败", ex); @@ -330,20 +337,6 @@ public class GatewaySecurityFilter extends OncePerRequestFilter { target.put("body", bodyText); } - private Object normalizeValue(Object value) { - if (value == null) { - return null; - } - if (value instanceof Map || value instanceof List) { - try { - return objectMapper.writeValueAsString(value); - } catch (JsonProcessingException ex) { - return value.toString(); - } - } - return value; - } - private String resolveEncryptionType(ApiClientCredentialDO credential, ApiGatewayProperties.Security security) { if (credential != null && StringUtils.hasText(credential.getEncryptionType())) { return credential.getEncryptionType(); @@ -481,12 +474,12 @@ public class GatewaySecurityFilter extends OncePerRequestFilter { response.resetBuffer(); response.setStatus(status.value()); String resolvedMessage = StringUtils.hasText(message) ? message : status.getReasonPhrase(); - String traceId = TracerUtils.getTraceId(); - ApiGatewayResponse envelope = ApiGatewayResponse.builder() + String traceId = TracerUtils.getTraceId(); + ApiGatewayResponse envelope = ApiGatewayResponse.builder() .code(status.value()) .message(resolvedMessage) .response(null) - .traceId(traceId) + .traceId(traceId) .build(); if (shouldEncryptErrorResponse(security, credential)) { String encryptionKey = credential.getEncryptionKey();