diff --git a/CLAUDE.md b/CLAUDE.md index 7f0585b..224d467 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -14,6 +14,7 @@ - `/sys/watchUserLogin` → **本地处理**:解密验证设备密码(防伪造)+ 单飞换 token + Redis 缓存 - 其余业务接口 → **手动 HttpUtils 透传**至源服务器 `http://10.10.10.228:29999` - token 失效三重自愈:10秒主动探活 + 预过期刷新 + 业务接口 401 被动兜底 +- **全局 CORS**:CorsFilter 处理跨域(设备后台跨域访问网关),OPTIONS 预检直接短路返回 ## 技术栈 diff --git a/src/main/java/com/renkang/gateway/filter/CorsFilter.java b/src/main/java/com/renkang/gateway/filter/CorsFilter.java new file mode 100644 index 0000000..84e1c8d --- /dev/null +++ b/src/main/java/com/renkang/gateway/filter/CorsFilter.java @@ -0,0 +1,52 @@ +package com.renkang.gateway.filter; + +import org.noear.solon.annotation.Component; +import org.noear.solon.core.handle.Context; +import org.noear.solon.core.handle.Filter; +import org.noear.solon.core.handle.FilterChain; + +/** + * 全局 CORS 跨域过滤器。 + *

+ * 健康设备后台(如 http://10.10.10.228:23001)跨域访问本网关时,浏览器要求响应带 + * Access-Control-Allow-Origin 等头,否则被拦截。 + *

+ * 处理策略(CORS 最佳实践,避免逐个枚举允许的头): + *

+ * 这样无论前端(jeecg vue3 等)加什么自定义头(X-Version、X-TIMESTAMP、X-Sign 等)都不会被拦。 + * + * @author renkang + */ +@Component(index = -200) // index 越小越先执行,确保 CORS 在业务透传前处理 +public class CorsFilter implements Filter { + + @Override + public void doFilter(Context ctx, FilterChain chain) throws Throwable { + // 统一设置基础 CORS 响应头(OPTIONS 和普通请求都需要) + ctx.headerSet("Access-Control-Allow-Origin", "*"); + ctx.headerSet("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, PATCH"); + // 动态回显客户端预检请求的头,避免逐个枚举(最佳实践) + String reqHeaders = ctx.header("Access-Control-Request-Headers"); + if (reqHeaders != null && !reqHeaders.isEmpty()) { + ctx.headerSet("Access-Control-Allow-Headers", reqHeaders); + } else { + // 无预检头时给一个兜底白名单(含 jeecg vue3 常用头) + ctx.headerSet("Access-Control-Allow-Headers", + "Content-Type, Authorization, X-Access-Token, X-Requested-With, X-Version, X-TIMESTAMP, X-Sign, token, Accept, Origin"); + } + ctx.headerSet("Access-Control-Max-Age", "3600"); + + // OPTIONS 预检请求直接短路返回,不透传源服务器(降低源服务器压力) + if ("OPTIONS".equalsIgnoreCase(ctx.method())) { + ctx.status(200); + return; // 不进入后续过滤器/Controller + } + + // 其他请求放行到后续处理(GatewayController 透传 / TokenController) + chain.doFilter(ctx); + } +}