From d20fa6ab009aca98a3cf3c20205a7a3571c278fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E5=AE=B6=E8=AE=AD?= Date: Thu, 3 Sep 2026 17:47:45 +0800 Subject: [PATCH] =?UTF-8?q?=E8=A7=A3=E5=86=B3=E8=B7=A8=E5=9F=9F=E8=AE=BF?= =?UTF-8?q?=E9=97=AE=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/CICD-DEPLOY.md | 31 +++++++++----- .../admin/config/WebSecurityConfig.java | 40 ++++++++++++++----- 2 files changed, 50 insertions(+), 21 deletions(-) diff --git a/docs/CICD-DEPLOY.md b/docs/CICD-DEPLOY.md index 54d8c4b..6227b9d 100644 --- a/docs/CICD-DEPLOY.md +++ b/docs/CICD-DEPLOY.md @@ -11,13 +11,13 @@ | 项 | 结果 | |---|---| | 触发方式 | push 到 master(另支持手动 workflow_dispatch 重跑) | -| 后端 freedom-admin | CI 构建 `freedom-admin:latest` → compose 部署 backend 容器(内网 8800) | -| 前端 freedom-web | CI 构建 `freedom-web:latest` → compose 部署 web 容器(宿主 8880 → 容器 80) | +| 后端 freedom-admin | CI 构建 `freedom-admin:latest` → compose 部署 backend 容器(宿主映射 `8800:8800`,API 直连) | +| 前端 freedom-web | CI 构建 `freedom-web:latest` → compose 部署 web 容器(宿主映射 `8880:80`) | | 统一入口 | `freedom-web`(nginx):`/api/*` 反代到 `backend:8800`(去前缀),其余走前端静态文件 | -| 公网访问 | frp 隧道:`http://124.221.214.237:8880` → `192.168.1.158:8880` | +| 公网访问 | frp 隧道:页面 `http://124.221.214.237:8880` ↔ `192.168.1.158:8880`;API 直连 `http://124.221.214.237:8800` ↔ `192.168.1.158:8800` | **验证通过的完整链路**: -`公网 :8880 → frp → freedom-web(nginx) → freedom-admin(SpringBoot) → MySQL` +`公网 :8880(页面) → frp → freedom-web(nginx) → freedom-admin(SpringBoot) → MySQL`;API 亦可直连公网 `:8800` --- @@ -164,7 +164,6 @@ job 容器的 `/workspace//` 是**空目录**(实测)。必须 - **首次构建慢**:基础镜像(maven/openjdk/node/nginx)从 docker.io 拉取慢(~10-20KB/s)。宿主 `/etc/docker/daemon.json` 已配 11 个 registry-mirrors,小镜像快、大镜像慢;首次忍一忍,之后走本地缓存。 - **Maven/npm 源**:Dockerfile 内分别固定阿里云 Maven 镜像与 npmmirror,避免依赖下载卡死。 - **compose 里或容器环境有历史遗留**:出现 `Found orphan containers ([wechatpadpro])` 警告属无害噪音。 -- **frpc 配置双重来源**:直接手改 `/opt/1panel/apps/frpc/frpc/data/frpc.toml` 后,若再经 frpc 管理界面(:7400)保存配置,界面会按自身存储重新生成整个文件,**手改内容会被覆盖**。改完端口后务必实测公网入口,并以界面最终生成的配置为准。 --- @@ -231,7 +230,8 @@ services: image: freedom-admin:${IMAGE_TAG:-latest} container_name: freedom-admin restart: unless-stopped - expose: ["8800"] # 仅内网,供 nginx 反代 + ports: + - "8800:8800" # 宿主 8800 → 容器 8800(API 直连入口;nginx 反代仍走内网同端口) environment: DB_URL: ${DB_URL} DB_USERNAME: ${DB_USERNAME} @@ -261,10 +261,18 @@ name = "freedom" type = "tcp" localIP = "192.168.1.158" localPort = 8880 # freedom-web 宿主端口 -remotePort = 8880 # 公网端口 +remotePort = 8880 # 公网端口(前端页面) + +[[proxies]] +name = "freedom-api" +type = "tcp" +localIP = "192.168.1.158" +localPort = 8800 # freedom-admin 宿主端口 +remotePort = 8800 # 公网端口(API 直连) ``` 改后 `docker restart 1Panel-frpc-hn0g` 生效。 +> 注:早期 `remotePort=8888` 版本已弃用,统一改为 `8880`(页面)/ `8800`(API)。 --- @@ -286,10 +294,11 @@ curl -s -X POST -H "Authorization: token " -H "Content-Type: application/ "http://127.0.0.1:3000/api/v1/repos/yiciheng/freedom-admin/actions/workflows/deploy.yml/dispatches" # 端到端验证 -curl -s -o /dev/null -w "%{http_code}\n" http://192.168.1.158:8880/ # 本机前端 -curl -s -o /dev/null -w "%{http_code}\n" http://124.221.214.237:8880/ # 公网前端 -curl -s -o /dev/null -w "%{http_code}\n" -X POST -d '{"username":"t","password":"t"}' \ - http://124.221.214.237:8880/api/login # 公网 API(经 nginx 去前缀转后端) +curl -s -o /dev/null -w "%{http_code}\n" http://192.168.1.158:8880/login # 本机前端 +curl -s -o /dev/null -w "%{http_code}\n" http://124.221.214.237:8880/login # 公网前端 +curl -s -o /dev/null -w "%{http_code}\n" http://124.221.214.237:8800/login # 公网 API 直连(后端) +curl -s -o /dev/null -w "%{http_code}\n" -X POST -d 'username=t&password=t' \ + http://124.221.214.237:8880/api/login # 公网经 nginx 反代到达后端 docker logs --tail 20 freedom-admin # 确认请求与 SQL 到达后端 # 判断 runner 配置是否真正加载(最关键的一步排查) diff --git a/src/main/java/com/freedom/admin/config/WebSecurityConfig.java b/src/main/java/com/freedom/admin/config/WebSecurityConfig.java index c7d0196..7759fdf 100644 --- a/src/main/java/com/freedom/admin/config/WebSecurityConfig.java +++ b/src/main/java/com/freedom/admin/config/WebSecurityConfig.java @@ -15,6 +15,8 @@ import org.springframework.security.web.authentication.UsernamePasswordAuthentic import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.CorsConfigurationSource; +import java.util.Collections; + /** * @author zhangjiaxun * @date 2021/1/5 17:40 @@ -100,23 +102,41 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter { } /** - * 跨越配置 + * 跨域配置 + * + * 生产环境页面与后端同源(web 容器 nginx 将 /api/* 反代到 backend),但浏览器对 + * 同源 POST 请求同样会携带 Origin 头,而 Spring 5.x 的 CORS 过滤器只要检测到 + * Origin 头即按跨域请求处理,来源不在白名单内会直接返回 403。 + * 这里动态放行: + * - 同源请求(Origin 的 host 与请求 Host 一致):回显该 Origin; + * - 跨域请求:仅放行本地开发来源 http://localhost:8080(npm run dev); + * - 无 Origin 的非浏览器请求:不做 CORS 限制。 */ @Bean public CorsConfigurationSource corsConfigurationSource() { - return httpServletRequest -> { + return request -> { + String origin = request.getHeader("Origin"); + if (origin == null) { + return null; // 非浏览器请求,不应用 CORS 处理 + } CorsConfiguration cfg = new CorsConfiguration(); + String reqHost = request.getHeader("Host"); + String originHost = origin.replaceFirst("^https?://", "").split("[:/]")[0]; + boolean sameOrigin = originHost != null && originHost.equalsIgnoreCase( + reqHost == null ? "" : reqHost.split(":")[0]); + cfg.setAllowCredentials(true); + if (sameOrigin) { + // 同源回显,确保经任意入口(如 8880 反代)访问都放行 + cfg.setAllowedOriginPatterns(Collections.singletonList(origin)); + } else if ("localhost".equalsIgnoreCase(originHost) || "127.0.0.1".equals(originHost)) { + cfg.setAllowedOriginPatterns(Collections.singletonList("http://localhost:8080")); + } else { + cfg.setAllowedOriginPatterns(Collections.emptyList()); + } + // 允许任何请求头/方法 cfg.addAllowedHeader("*"); cfg.addAllowedMethod("*"); - // 设置允许跨域请求的域名 - cfg.addAllowedOrigin("http://localhost:8080"); - // 允许任何请求 - cfg.addAllowedHeader("*"); cfg.addExposedHeader("token"); - // 是否允许证书 - cfg.setAllowCredentials(true); - // - cfg.checkOrigin("http://localhost:8080"); return cfg; }; }