解决跨域访问问题
CI/CD Deploy (Admin) / 构建后端镜像并部署 (push) Successful in 1m39s

This commit is contained in:
张家训
2026-09-03 17:47:45 +08:00
parent 9f5e6920ac
commit d20fa6ab00
2 changed files with 50 additions and 21 deletions
+20 -11
View File
@@ -11,13 +11,13 @@
| 项 | 结果 | | 项 | 结果 |
|---|---| |---|---|
| 触发方式 | push 到 master(另支持手动 workflow_dispatch 重跑) | | 触发方式 | push 到 master(另支持手动 workflow_dispatch 重跑) |
| 后端 freedom-admin | CI 构建 `freedom-admin:latest` → compose 部署 backend 容器(内网 8800) | | 后端 freedom-admin | CI 构建 `freedom-admin:latest` → compose 部署 backend 容器(宿主映射 `8800:8800`,API 直连) |
| 前端 freedom-web | CI 构建 `freedom-web:latest` → compose 部署 web 容器(宿主 8880 → 容器 80) | | 前端 freedom-web | CI 构建 `freedom-web:latest` → compose 部署 web 容器(宿主映射 `8880:80`) |
| 统一入口 | `freedom-web`(nginx):`/api/*` 反代到 `backend:8800`(去前缀),其余走前端静态文件 | | 统一入口 | `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/<owner>/<repo>` 是**空目录**(实测)。必须
- **首次构建慢**:基础镜像(maven/openjdk/node/nginx)从 docker.io 拉取慢(~10-20KB/s)。宿主 `/etc/docker/daemon.json` 已配 11 个 registry-mirrors,小镜像快、大镜像慢;首次忍一忍,之后走本地缓存。 - **首次构建慢**:基础镜像(maven/openjdk/node/nginx)从 docker.io 拉取慢(~10-20KB/s)。宿主 `/etc/docker/daemon.json` 已配 11 个 registry-mirrors,小镜像快、大镜像慢;首次忍一忍,之后走本地缓存。
- **Maven/npm 源**:Dockerfile 内分别固定阿里云 Maven 镜像与 npmmirror,避免依赖下载卡死。 - **Maven/npm 源**:Dockerfile 内分别固定阿里云 Maven 镜像与 npmmirror,避免依赖下载卡死。
- **compose 里或容器环境有历史遗留**:出现 `Found orphan containers ([wechatpadpro])` 警告属无害噪音。 - **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} image: freedom-admin:${IMAGE_TAG:-latest}
container_name: freedom-admin container_name: freedom-admin
restart: unless-stopped restart: unless-stopped
expose: ["8800"] # 仅内网,供 nginx 反代 ports:
- "8800:8800" # 宿主 8800 → 容器 8800(API 直连入口;nginx 反代仍走内网同端口)
environment: environment:
DB_URL: ${DB_URL} DB_URL: ${DB_URL}
DB_USERNAME: ${DB_USERNAME} DB_USERNAME: ${DB_USERNAME}
@@ -261,10 +261,18 @@ name = "freedom"
type = "tcp" type = "tcp"
localIP = "192.168.1.158" localIP = "192.168.1.158"
localPort = 8880 # freedom-web 宿主端口 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` 生效。 改后 `docker restart 1Panel-frpc-hn0g` 生效。
> 注:早期 `remotePort=8888` 版本已弃用,统一改为 `8880`(页面)/ `8800`(API)。
--- ---
@@ -286,10 +294,11 @@ curl -s -X POST -H "Authorization: token <TOKEN>" -H "Content-Type: application/
"http://127.0.0.1:3000/api/v1/repos/yiciheng/freedom-admin/actions/workflows/deploy.yml/dispatches" "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://192.168.1.158:8880/login # 本机前端
curl -s -o /dev/null -w "%{http_code}\n" http://124.221.214.237:8880/ # 公网前端 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" -X POST -d '{"username":"t","password":"t"}' \ curl -s -o /dev/null -w "%{http_code}\n" http://124.221.214.237:8800/login # 公网 API 直连(后端)
http://124.221.214.237:8880/api/login # 公网 API(经 nginx 去前缀转后端) 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 到达后端 docker logs --tail 20 freedom-admin # 确认请求与 SQL 到达后端
# 判断 runner 配置是否真正加载(最关键的一步排查) # 判断 runner 配置是否真正加载(最关键的一步排查)
@@ -15,6 +15,8 @@ import org.springframework.security.web.authentication.UsernamePasswordAuthentic
import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource; import org.springframework.web.cors.CorsConfigurationSource;
import java.util.Collections;
/** /**
* @author zhangjiaxun * @author zhangjiaxun
* @date 2021/1/5 17:40 * @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 @Bean
public CorsConfigurationSource corsConfigurationSource() { public CorsConfigurationSource corsConfigurationSource() {
return httpServletRequest -> { return request -> {
String origin = request.getHeader("Origin");
if (origin == null) {
return null; // 非浏览器请求,不应用 CORS 处理
}
CorsConfiguration cfg = new CorsConfiguration(); 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.addAllowedHeader("*");
cfg.addAllowedMethod("*"); cfg.addAllowedMethod("*");
// 设置允许跨域请求的域名
cfg.addAllowedOrigin("http://localhost:8080");
// 允许任何请求
cfg.addAllowedHeader("*");
cfg.addExposedHeader("token"); cfg.addExposedHeader("token");
// 是否允许证书
cfg.setAllowCredentials(true);
//
cfg.checkOrigin("http://localhost:8080");
return cfg; return cfg;
}; };
} }