fix: 修复工作流公共 API 调用问题

- 限制远程文档仅访问公网地址并校验重定向目标

- 统一访问令牌 401/403 与过期执行状态 404 语义

- 校正节点查询参数和工作流状态文档
This commit is contained in:
2026-07-31 11:27:37 +08:00
parent 1cbee6b018
commit 41b056b7e3
12 changed files with 953 additions and 33 deletions

View File

@@ -25,8 +25,9 @@ public class PublicApiInterceptor implements HandlerInterceptor {
String requestURI = request.getRequestURI();
String apiKey = request.getHeader("ApiKey");
if (apiKey == null || apiKey.isEmpty()) {
if (apiKey == null || apiKey.isBlank()) {
Result<Void> failed = Result.fail(401, "密钥不正确");
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
ResponseUtil.renderJson(response, failed);
return false;
}

View File

@@ -0,0 +1,85 @@
package tech.easyflow.publicapi.interceptor;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.junit.Assert;
import org.junit.Test;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.Proxy;
import java.util.concurrent.atomic.AtomicInteger;
/**
* {@link PublicApiInterceptor} 鉴权响应测试。
*/
public class PublicApiInterceptorTest {
/**
* 验证缺少访问令牌时返回真实 HTTP 401 和统一错误体。
*
* @throws Exception 拦截器处理失败时抛出
*/
@Test
public void shouldReturnUnauthorizedWhenApiKeyMissing() throws Exception {
StringWriter body = new StringWriter();
AtomicInteger status = new AtomicInteger();
HttpServletRequest request = proxy(
HttpServletRequest.class,
(instance, method, args) -> {
if ("getRequestURI".equals(method.getName())) {
return "/public-api/workflow/runAsync";
}
if ("getHeader".equals(method.getName())) {
return null;
}
throw new AssertionError(
"测试路径不应调用 HttpServletRequest."
+ method.getName());
});
HttpServletResponse response = proxy(
HttpServletResponse.class,
(instance, method, args) -> {
if ("setStatus".equals(method.getName())) {
status.set((Integer) args[0]);
return null;
}
if ("setContentType".equals(method.getName())) {
return null;
}
if ("getWriter".equals(method.getName())) {
return new PrintWriter(body);
}
throw new AssertionError(
"测试路径不应调用 HttpServletResponse."
+ method.getName());
});
boolean allowed = new PublicApiInterceptor()
.preHandle(request, response, new Object());
Assert.assertFalse(allowed);
Assert.assertEquals(
HttpServletResponse.SC_UNAUTHORIZED,
status.get());
Assert.assertTrue(body.toString().contains("\"errorCode\":401"));
Assert.assertTrue(body.toString().contains("密钥不正确"));
}
/**
* 创建接口代理。
*
* @param type 接口类型
* @param handler 调用处理器
* @param <T> 接口类型
* @return 代理实例
*/
private <T> T proxy(
Class<T> type,
java.lang.reflect.InvocationHandler handler) {
return type.cast(Proxy.newProxyInstance(
type.getClassLoader(),
new Class<?>[]{type},
handler));
}
}