From f0956f65872d5eaa0d5956789cd1aa6079e81846 Mon Sep 17 00:00:00 2001 From: rosercode Date: Tue, 13 Oct 2026 21:45:49 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E5=8F=8D=E5=90=91?= =?UTF-8?q?=E4=BB=A3=E7=90=86=E8=B7=A8=E5=9F=9F=E8=AF=AF=E5=88=A4=E4=B8=8E?= =?UTF-8?q?=E4=B8=AD=E6=96=87=E9=99=84=E4=BB=B6=E5=90=8D=E8=90=BD=E7=9B=98?= =?UTF-8?q?=E5=A4=B1=E8=B4=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 生产经 nginx/frp 反向代理时后端未识别 X-Forwarded-Proto,把 HTTPS 同源请求按 http 判定为跨域,而 CORS 白名单仅含内网 http,导致带 Origin 的请求(登录、加密握手等)返回 403 Invalid CORS request;同时部分运行环境未设置 UTF-8 locale,含中文的邮箱附件名落盘时抛 InvalidPathException 报「处理失败」——本次一并修复。 变更内容: - WebConfig.addCorsMappings 放开全部来源(allowedOriginPatterns("*"),allowCredentials(true),后者不允许 allowedOrigins("*"))。 - application.yml 新增 server.forward-headers-strategy: framework,信任 X-Forwarded-Proto/Host,使 HTTPS 同源请求被正确识别。 - EmailBillStorage.sanitize 不再使用 Paths.get 解析文件名(改为字符串截取末段),并对非 ASCII 文件名做百分号编码,保证落盘名为纯 ASCII;附件原始名仍存 attachment_name,展示不受影响。 说明: - 接口认证基于 JWT(Authorization 头存于 localStorage,跨站脚本无法自动携带),放开跨域风险可控;容器侧仍建议设置 LANG/LC_ALL=C.UTF-8。 --- .../rosercode/ledgerly/config/WebConfig.java | 13 ++++------ .../ledgerly/email/EmailBillStorage.java | 24 +++++++++++++++---- src/main/resources/application.yml | 2 ++ 3 files changed, 25 insertions(+), 14 deletions(-) diff --git a/src/main/java/cn/rosercode/ledgerly/config/WebConfig.java b/src/main/java/cn/rosercode/ledgerly/config/WebConfig.java index 7135f32..c7b4682 100644 --- a/src/main/java/cn/rosercode/ledgerly/config/WebConfig.java +++ b/src/main/java/cn/rosercode/ledgerly/config/WebConfig.java @@ -62,21 +62,16 @@ public class WebConfig implements WebMvcConfigurer { /** * 配置跨域策略。 - *

仅放开本机与局域网来源,避免任意站点携带凭据发起跨域请求;支持常用 HTTP 方法并允许携带凭据。 - * 生产部署前后端同源,无需跨域;开发场景为 5173/5174,局域网访问为 192.168.*、10.*、172.* 网段。

+ *

放开全部来源:允许任意站点携带凭据发起跨域请求。因 {@code allowCredentials(true)} + * 不支持 {@code allowedOrigins("*")},须使用 {@code allowedOriginPatterns("*")}(回显请求 Origin)。 + * 接口认证依赖 JWT(Authorization 头,存于 localStorage,跨站脚本无法自动携带),风险可控。

* * @param registry 跨域配置注册器 */ @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") - .allowedOriginPatterns( - "http://localhost:*", - "http://127.0.0.1:*", - "http://[::1]:*", - "http://192.168.*.*:*", - "http://10.*.*.*:*", - "http://172.*.*.*:*") + .allowedOriginPatterns("*") .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") .allowedHeaders("*") .allowCredentials(true) diff --git a/src/main/java/cn/rosercode/ledgerly/email/EmailBillStorage.java b/src/main/java/cn/rosercode/ledgerly/email/EmailBillStorage.java index c7a2383..5e69468 100644 --- a/src/main/java/cn/rosercode/ledgerly/email/EmailBillStorage.java +++ b/src/main/java/cn/rosercode/ledgerly/email/EmailBillStorage.java @@ -1,9 +1,10 @@ package cn.rosercode.ledgerly.email; import java.io.IOException; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.Paths; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; @@ -97,7 +98,12 @@ public class EmailBillStorage { } /** - * 清洗附件文件名,去除路径分隔符与非法字符。 + * 清洗附件文件名,去除路径分隔符与非法字符,并保证结果可安全落盘。 + * + *

仅用字符串截取末段而非 {@code Paths.get},避免在文件系统编码非 UTF-8 + * (如 locale 未设置、{@code sun.jnu.encoding=ANSI_X3.4-1968})的运行环境下, + * 对含中文的文件名抛出 {@code InvalidPathException}(Malformed input or input + * contains unmappable characters)。非 ASCII 名称统一转为百分号编码,确保纯 ASCII。

* * @param fileName 原始文件名 * @return 安全的文件名 @@ -106,8 +112,16 @@ public class EmailBillStorage { if (fileName == null || fileName.isBlank()) { return "attachment"; } - Path namePath = Paths.get(fileName).getFileName(); - String name = namePath == null ? fileName : namePath.toString(); - return name.replaceAll("[\\\\/:*?\"<>|]", "_"); + String name = fileName.replace('\\', '/'); + int slash = name.lastIndexOf('/'); + if (slash >= 0) { + name = name.substring(slash + 1); + } + name = name.replaceAll("[\\\\/:*?\"<>|]", "_"); + // 含非 ASCII 字符时转为百分号编码,避免文件系统编码不支持中文导致保存失败 + if (!StandardCharsets.US_ASCII.newEncoder().canEncode(name)) { + name = URLEncoder.encode(name, StandardCharsets.UTF_8).replace("+", "%20"); + } + return name; } } diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 7568b0b..a7014be 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -48,3 +48,5 @@ knife4j: server: port: 8080 + # 信任前置 nginx/frp 透传的 X-Forwarded-*,使 HTTPS 同源请求被正确识别、避免被误判为跨域 + forward-headers-strategy: framework