Merge branch 'dev' into 'test'

修复数据总线访问日志无法显示状态码问题:...

See merge request jygk/dsc!22
This commit is contained in:
朝锦 杨
2026-01-22 02:40:04 +00:00
19 changed files with 677 additions and 15 deletions

View File

@@ -11,6 +11,7 @@
<module>zt-module-databus-api</module>
<module>zt-module-databus-server</module>
<module>zt-module-databus-server-app</module>
<module>zt-module-databus-client</module>
</modules>
<modelVersion>4.0.0</modelVersion>

View File

@@ -0,0 +1,152 @@
package com.zt.plat.module.databus.api.dto;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.zt.plat.framework.common.util.json.databind.TimestampLocalDateTimeSerializer;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.time.LocalDateTime;
/**
* 新增Databus API 访问日志请求体。
*/
@Data
@EqualsAndHashCode
public class ApiAccessLogCreateReq {
/**
* 主键
*/
@Schema(description = "主键")
private Long id;
/**
* HTTP 方法
*/
@Schema(description = "HTTP 方法")
@NotNull(message = "HTTP 方法不能为空")
private String requestMethod;
/**
* 请求路径
*/
@Schema(description = "请求路径")
@NotNull(message = "请求路径不能为空")
private String requestPath;
/**
* 调用使用的应用标识
*/
@Schema(description = "调用使用的应用标识")
@NotNull(message = "调用使用的应用标识不能为空")
private String credentialAppId;
/**
* 多租户编号
*/
@Schema(description = "多租户编号")
@NotNull(message = "多租户编号不能为空")
private Long tenantId;
/**
* 查询参数JSON 字符串)
*/
@Schema(description = "查询参数JSON 字符串)")
private String requestQuery;
/**
* 请求头信息JSON 字符串)
*/
@Schema(description = "请求头信息JSON 字符串)")
private String requestHeaders;
/**
* 请求体JSON 字符串)
*/
@Schema(description = "请求体JSON 字符串)")
private String requestBody;
/**
* 响应 HTTP 状态码
*/
@Schema(description = "响应 HTTP 状态码")
private Integer responseStatus;
/**
* 响应提示信息
*/
@Schema(description = "响应提示信息")
private String responseMessage;
/**
* 响应体JSON 字符串)
*/
@Schema(description = "响应体JSON 字符串)")
private String responseBody;
/**
* 访问状态0-成功 1-客户端错误 2-服务端错误 3-未知
*/
@Schema(description = "访问状态0-成功 1-客户端错误 2-服务端错误 3-未知")
@NotNull(message = "访问状态不能为空")
private Integer status;
/**
* 业务错误码
*/
@Schema(description = "业务错误码")
private String errorCode;
/**
* 错误信息
*/
@Schema(description = "错误信息")
private String errorMessage;
/**
* 异常堆栈
*/
@Schema(description = "异常堆栈")
private String exceptionStack;
/**
* 客户端 IP
*/
@Schema(description = "客户端 IP")
private String clientIp;
/**
* User-Agent
*/
@Schema(description = "User-Agent")
private String userAgent;
/**
* 请求耗时(毫秒)
*/
@Schema(description = "请求耗时(毫秒)")
private Long duration;
/**
* 请求时间
*/
@Schema(description = "请求时间")
@JsonSerialize(using = TimestampLocalDateTimeSerializer.class)
private LocalDateTime requestTime;
/**
* 响应时间
*/
@Schema(description = "响应时间")
@JsonSerialize(using = TimestampLocalDateTimeSerializer.class)
private LocalDateTime responseTime;
/**
* 额外调试信息JSON 字符串)
*/
@Schema(description = "额外调试信息JSON 字符串)")
private String extra;
}

View File

@@ -0,0 +1,28 @@
package com.zt.plat.module.databus.api.provider;
import com.zt.plat.framework.common.pojo.CommonResult;
import com.zt.plat.module.databus.api.dto.ApiAccessLogCreateReq;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import java.util.Map;
/**
* Databus API访问日志接口
* 2026/1/20 16:26
*/
@FeignClient(name = "${databus.provider.log.service:databus-server}")
@Tag(name = "RPC 服务 - Databus API访问日志接口")
public interface DatabusAccessLogProviderApi {
String PREFIX = "/databus/api/portal/access-log";
@PostMapping(PREFIX + "/add")
@Operation(summary = "新增访问日志")
CommonResult<Boolean> add(@RequestHeader Map<String, String> headers, @RequestBody ApiAccessLogCreateReq req);
}

View File

@@ -0,0 +1,82 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>zt-module-databus</artifactId>
<groupId>com.zt.plat</groupId>
<version>${revision}</version>
</parent>
<artifactId>zt-module-databus-server-client</artifactId>
<packaging>jar</packaging>
<name>${project.artifactId}</name>
<description>
Databus client, 提供调用第三方服务的能力并记录调用日志。
</description>
<dependencies>
<dependency>
<groupId>com.zt.plat</groupId>
<artifactId>zt-module-databus-api</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,242 @@
package com.zt.plat.module.databus.client;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import cn.hutool.http.Method;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.zt.plat.framework.common.pojo.CommonResult;
import com.zt.plat.framework.common.util.security.CryptoSignatureUtils;
import com.zt.plat.module.databus.api.dto.ApiAccessLogCreateReq;
import com.zt.plat.module.databus.api.provider.DatabusAccessLogProviderApi;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
/**
* 数据总线提供的 http 客户端, 通过此客户端发起接口调用,会自动记录请求日志到数据总线
* 2026/1/20 09:44
*/
@Component
@Slf4j
public class DatabusClient {
/**
* 多租户编号
*/
@Value("${zt.plat.databus.client.tenantId:1}")
private Long tenantId;
@Resource
private DatabusAccessLogProviderApi databusAccessLogProviderApi;
private static final int MAX_TEXT_LENGTH = 4000;
/**
* 发送 get 请求
* @param urlString 仅接口地址,不带参数,参数由data提供
* @param data 请求参数
* @param headers 请求头
* @return 响应结果
*/
public String get(String urlString, Map<String, Object> data, Map<String, String> headers, String appId, String authToken) {
return doRequest(urlString, data, headers, Method.GET, appId, authToken);
}
/**
* 发送 post 请求
* @param urlString 仅接口地址,不带参数,参数由data提供
* @param data 请求数据
* @param headers 请求头
* @return 响应结果
*/
public String post(String urlString, Map<String, Object> data, Map<String, String> headers, String appId, String authToken) {
return doRequest(urlString, data, headers, Method.POST, appId, authToken);
}
/**
* 发送 put 请求
* @param urlString 仅接口地址,不带参数,参数由data提供
* @param data 请求数据
* @param headers 请求头
* @return 响应结果
*/
public String put(String urlString, Map<String, Object> data, Map<String, String> headers, String appId, String authToken) {
return doRequest(urlString, data, headers, Method.PUT, appId, authToken);
}
/**
* 发送 delete 请求
* @param urlString 仅接口地址,不带参数,参数由data提供
* @param data 请求数据
* @param headers 请求头
* @return 响应结果
*/
public String delete(String urlString, Map<String, Object> data, Map<String, String> headers, String appId, String authToken) {
return doRequest(urlString, data, headers, Method.DELETE, appId, authToken);
}
/**
* 发送请求到门户(token模式,不证书加密)
* @param urlString 仅接口地址,不带参数,参数由data提供
* @param data 请求数据
* @param headers 请求头
* @param method 请求方式,默认为 GET
* @return 响应结果
*/
public String doRequest(String urlString, Map<String, Object> data, Map<String, String> headers, Method method, String appId, String authToken) {
if (method == null) {
method = Method.GET;
}
Assert.hasText(urlString, "接口地址不能为空");
HttpRequest request;
ApiAccessLogCreateReq logReq = new ApiAccessLogCreateReq();
if (Method.GET.equals(method) || Method.DELETE.equals(method)) {
logReq.setRequestQuery(JSONUtil.toJsonStr(data));
} else {
logReq.setRequestBody(JSONUtil.toJsonStr(data));
}
request = HttpUtil.createRequest(method, urlString).form(data);
if (headers != null && !headers.isEmpty()) {
for (Map.Entry<String, String> entry : headers.entrySet()) {
request.header(entry.getKey(), entry.getValue(), true);
}
}
logReq.setTenantId(tenantId);
logReq.setRequestMethod(method.name());
logReq.setRequestPath(urlString);
logReq.setCredentialAppId(appId);
logReq.setRequestHeaders(JSONUtil.toJsonStr(headers));
logReq.setUserAgent(request.header("User-Agent"));
String result;
logReq.setRequestTime(LocalDateTime.now());
long requestTime = System.currentTimeMillis();
try (HttpResponse response = request.execute()) {
logReq.setDuration(System.currentTimeMillis() - requestTime);
logReq.setResponseTime(LocalDateTime.now());
result = response.body();
logReq.setResponseStatus(response.getStatus());
logReq.setResponseBody(result);
logReq.setStatus(resolveStatus(response.getStatus()));
Map<String, String> errorCodeAndMsg = extractErrorCodeAndMsg(result, response.getStatus());
logReq.setErrorCode(errorCodeAndMsg.get("errorCode"));
logReq.setErrorMessage(errorCodeAndMsg.get("errorMessage"));
addAccessLog(logReq, appId, authToken);
} catch (Exception e) {
// 错误的日志服务端记录了,这里就不再记录了
// logReq.setStatus(1);
// logReq.setExceptionStack(buildStackTrace( e));
// addAccessLog(logReq, appId, authToken);
throw new RuntimeException(e);
}
return result;
}
private void addAccessLog(ApiAccessLogCreateReq logReq, String appId, String authToken) {
String nonce = randomNonce();
Map<String, String> headers = new HashMap<>();
headers.put("tenant-id", String.valueOf(tenantId));
headers.put("ZT-App-Id", appId);
headers.put("ZT-Timestamp", Long.toString(System.currentTimeMillis()));
headers.put("ZT-Nonce", nonce);
headers.put("ZT-Auth-Token", authToken);
CommonResult<Boolean> response = databusAccessLogProviderApi.add(headers, logReq);
if (response.getCode() != 0) {
throw new RuntimeException("添加访问日志失败: " + response);
}
}
private static String randomNonce() {
return UUID.randomUUID().toString().replace("-", "");
}
private String buildStackTrace(Throwable throwable) {
try (StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw)) {
throwable.printStackTrace(pw);
return truncate(sw.toString());
} catch (Exception ex) {
return throwable.getMessage();
}
}
private Integer resolveStatus(Integer httpStatus) {
if (httpStatus == null) {
return 3;
}
if (httpStatus >= 200 && httpStatus < 400) {
return 0;
}
if (httpStatus >= 400 && httpStatus < 500) {
return 1;
}
if (httpStatus >= 500) {
return 2;
}
return 3;
}
private Map<String, String> extractErrorCodeAndMsg(String responseBody, Integer responseStatus) {
Map<String, String> result = new HashMap<>();
if (!isErrorStatus(responseStatus)) {
return result;
}
if (JSONUtil.isTypeJSONObject(responseBody)) {
JSONObject map = JSONUtil.parseObj(responseBody);
Object errorCode = firstNonNull(map.get("errorCode"), map.get("code"));
errorCode = errorCode == null ? null : truncate(String.valueOf(errorCode));
if (errorCode != null) {
result.put("errorCode", errorCode.toString());
}
Object message = firstNonNull(map.get("errorMessage"), map.get("message"));
if (message != null) {
message = truncate(String.valueOf(message));
result.put("errorMessage", message.toString());
}
}
return result;
}
private boolean isErrorStatus(Integer responseStatus) {
return responseStatus != null && responseStatus >= 400;
}
@SafeVarargs
private <T> T firstNonNull(T... candidates) {
if (candidates == null) {
return null;
}
for (T candidate : candidates) {
if (candidate != null) {
return candidate;
}
}
return null;
}
private String truncate(String text) {
if (!StringUtils.hasText(text)) {
return text;
}
if (text.length() <= MAX_TEXT_LENGTH) {
return text;
}
return text.substring(0, MAX_TEXT_LENGTH);
}
}

View File

@@ -0,0 +1,16 @@
package com.zt.plat.module.databus.client;
/**
*
* 2026/1/21 10:48
*/
import com.zt.plat.module.databus.api.provider.DatabusAccessLogProviderApi;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Configuration;
@Configuration(proxyBeanMethods = false)
@EnableFeignClients(clients = {DatabusAccessLogProviderApi.class})
public class RpcConfiguration {
}

View File

@@ -0,0 +1,3 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.zt.plat.module.databus.client.DatabusClient,\
com.zt.plat.module.databus.client.RpcConfiguration

View File

@@ -0,0 +1,24 @@
package com.zt.plat.module.databus;
import com.zt.plat.module.databus.client.DatabusClient;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
/**
*
* 2026/1/20 14:29
*/
@SpringBootTest(classes = TestApplication.class)
public class DatabusClientTest {
@Autowired
private DatabusClient databusClient;
@Test
void test() {
String result = databusClient.get("https://www.baidu.com/", null, null, "jwyw2", "a5d7cf609c0b47038ea405c660726ee9");
System.out.println(result);
}
}

View File

@@ -0,0 +1,26 @@
package com.zt.plat.module.databus;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
/**
*
* 2026/1/20 14:26
*/
@SpringBootTest
@SpringBootApplication(exclude = {
DataSourceAutoConfiguration.class,
DataSourceTransactionManagerAutoConfiguration.class,
HibernateJpaAutoConfiguration.class,
JdbcTemplateAutoConfiguration.class,
})
public class TestApplication {
public static void main(String[] args) {
SpringApplication.run(TestApplication.class, args);
}
}

View File

@@ -0,0 +1,11 @@
spring:
cloud:
nacos:
server-addr: 172.16.46.63:30848 # Nacos 服务器地址
username: nacos # Nacos 账号
password: P@ssword25 # Nacos 密码
discovery: # 【配置中心】配置项
namespace: klw # 命名空间。这里使用 maven Profile 资源过滤进行动态替换
group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP
metadata:
version: 1.0.0 # 服务实例的版本号,可用于灰度发布

View File

@@ -0,0 +1,45 @@
package com.zt.plat.module.databus.api;
import com.zt.plat.framework.common.pojo.CommonResult;
import com.zt.plat.framework.common.util.monitor.TracerUtils;
import com.zt.plat.framework.common.util.object.BeanUtils;
import com.zt.plat.framework.common.util.servlet.ServletUtils;
import com.zt.plat.module.databus.api.dto.ApiAccessLogCreateReq;
import com.zt.plat.module.databus.api.provider.DatabusAccessLogProviderApi;
import com.zt.plat.module.databus.dal.dataobject.gateway.ApiAccessLogDO;
import com.zt.plat.module.databus.service.gateway.ApiAccessLogService;
import jakarta.annotation.Resource;
import jakarta.annotation.security.PermitAll;
import org.springframework.web.bind.annotation.RestController;
import java.time.LocalDateTime;
import java.util.Map;
import static com.zt.plat.framework.common.pojo.CommonResult.success;
/**
*
* 2026/1/21 10:05
*/
@RestController
public class DatabusAccessLogProviderApiImpl implements DatabusAccessLogProviderApi {
@Resource
private ApiAccessLogService apiAccessLogService;
@Override
@PermitAll
public CommonResult<Boolean> add(Map<String, String> headers, ApiAccessLogCreateReq req) {
ApiAccessLogDO logDO = new ApiAccessLogDO();
BeanUtils.copyProperties(req, logDO);
logDO.setTraceId(TracerUtils.getTraceId());
logDO.setClientIp(ServletUtils.getClientIP());
logDO.setCreateTime(LocalDateTime.now());
logDO.setUpdateTime(LocalDateTime.now());
logDO.setCreator("1");
logDO.setUpdater("1");
apiAccessLogService.create(logDO);
return success(Boolean.TRUE);
}
}

View File

@@ -159,6 +159,7 @@ public class ApiGatewayAccessLogger {
update.setStatus(resolveStatus(status));
update.setResponseTime(LocalDateTime.now());
update.setDuration(calculateDuration(request));
update.setUpdater("1");
apiAccessLogService.update(update);
} catch (Exception ex) {
log.warn("更新入口 API 访问日志失败, logId={}", logId, ex);

View File

@@ -89,6 +89,7 @@ public class ApiGatewayExecutionService {
} else {
responseContext = apiFlowDispatcher.dispatch(context.getApiCode(), context.getApiVersion(), context);
}
responseContext.setResponseStatus(resolveStatus(responseContext));
} catch (ServiceException ex) {
errorProcessor.applyServiceException(context, ex);
accessLogger.onException(context, ex);

View File

@@ -77,6 +77,9 @@ public class GatewaySecurityFilter extends OncePerRequestFilter {
filterChain.doFilter(request, response);
return;
}
// 添加日志接口需要特殊处理
boolean isNormalProcess = !pathMatcher.match("/databus/api/portal/access-log/**", pathWithinApplication);
Long accessLogId = null;
ApiGatewayProperties.Security security = properties.getSecurity();
ApiClientCredentialDO credential = null;
@@ -95,8 +98,10 @@ public class GatewaySecurityFilter extends OncePerRequestFilter {
accessLogger.finalizeEarly(request, HttpStatus.FORBIDDEN.value(), "IP 禁止访问");
return;
}
// IP 校验通过后再补录入口日志,避免无租户信息写库
accessLogId = accessLogger.logEntrance(request);
// IP 校验通过后再补录入口日志,避免无租户信息写库, 非日志添加接口才记录日志
if (isNormalProcess) {
accessLogId = accessLogger.logEntrance(request);
}
if (!security.isEnabled()) {
byte[] originalBody = StreamUtils.copyToByteArray(request.getInputStream());
CachedBodyHttpServletRequest passthroughRequest = new CachedBodyHttpServletRequest(request, originalBody);
@@ -128,13 +133,17 @@ public class GatewaySecurityFilter extends OncePerRequestFilter {
if (nonce.length() < 8) {
throw new SecurityValidationException(HttpStatus.BAD_REQUEST, "随机数长度不足");
}
String signature = requireHeader(request, SIGNATURE_HEADER, "缺少签名");
// 尝试按凭证配置解密请求体,并构建签名载荷进行校验
byte[] decryptedBody = decryptRequestBody(requestBody, credential, security);
verifySignature(request, decryptedBody, signature, credential, security, appId, timestampHeader);
if (isNormalProcess) {
// 非日志添加接口才处理
String signature = requireHeader(request, SIGNATURE_HEADER, "缺少签名");
// 尝试按凭证配置解密请求体,并构建签名载荷进行校验
byte[] decryptedBody = decryptRequestBody(requestBody, credential, security);
verifySignature(request, decryptedBody, signature, credential, security, appId, timestampHeader);
requestBody = decryptedBody;
}
ensureNonce(tenantId, appId, nonce, security);
requestBody = decryptedBody;
}
// 使用可重复读取的请求包装,供后续过滤器继续消费
@@ -154,7 +163,9 @@ public class GatewaySecurityFilter extends OncePerRequestFilter {
try {
filterChain.doFilter(securedRequest, responseWrapper);
dispatchedToGateway = true;
encryptResponse(responseWrapper, credential, security);
if (isNormalProcess) {
encryptResponse(responseWrapper, credential, security);
}
} finally {
responseWrapper.copyBodyToResponse();
}
@@ -163,7 +174,7 @@ public class GatewaySecurityFilter extends OncePerRequestFilter {
accessLogId = accessLogger.logEntrance(request);
}
log.warn("[API-PORTAL] 安全校验失败: {}", ex.getMessage());
writeErrorResponse(response, security, credential, ex.status(), ex.getMessage());
writeErrorResponse(response, security, credential, ex.status(), ex.getMessage(), isNormalProcess);
if (!dispatchedToGateway) {
accessLogger.finalizeEarly(request, ex.status().value(), ex.getMessage());
}
@@ -172,7 +183,7 @@ public class GatewaySecurityFilter extends OncePerRequestFilter {
accessLogId = accessLogger.logEntrance(request);
}
log.error("[API-PORTAL] 处理安全校验时出现异常", ex);
writeErrorResponse(response, security, credential, HttpStatus.INTERNAL_SERVER_ERROR, "网关安全校验失败");
writeErrorResponse(response, security, credential, HttpStatus.INTERNAL_SERVER_ERROR, "网关安全校验失败", isNormalProcess);
if (!dispatchedToGateway) {
accessLogger.finalizeEarly(request, HttpStatus.INTERNAL_SERVER_ERROR.value(), "网关安全校验失败");
}
@@ -479,6 +490,7 @@ public class GatewaySecurityFilter extends OncePerRequestFilter {
String token = tokenOptional.get();
securedRequest.setHeader(HttpHeaders.AUTHORIZATION, "Bearer " + token);
securedRequest.setHeader(GatewayJwtResolver.HEADER_ZT_AUTH_TOKEN, token);
}
private static final class SecurityValidationException extends RuntimeException {
@@ -499,7 +511,8 @@ public class GatewaySecurityFilter extends OncePerRequestFilter {
ApiGatewayProperties.Security security,
ApiClientCredentialDO credential,
HttpStatus status,
String message) {
String message,
boolean isNormalProcess) {
if (response.isCommitted()) {
log.warn("[API-PORTAL] 响应已提交,无法写入安全校验错误: {}", message);
return;
@@ -514,7 +527,7 @@ public class GatewaySecurityFilter extends OncePerRequestFilter {
.response(null)
.traceId(traceId)
.build();
if (shouldEncryptErrorResponse(security, credential)) {
if (shouldEncryptErrorResponse(security, credential) && isNormalProcess) {
String encryptionKey = credential.getEncryptionKey();
String encryptionType = resolveEncryptionType(credential, security);
try {