From 84448b30640127e8c64e49c19d4445da254718de Mon Sep 17 00:00:00 2001 From: wangjia <809946525@qq.com> Date: Tue, 7 Jul 2026 23:03:39 +0800 Subject: [PATCH] =?UTF-8?q?fix(server/cors):=20=E6=94=BE=E8=A1=8C=20X-Refr?= =?UTF-8?q?esh-Token=20=E5=A4=B4(=E4=BF=AE=E7=94=A8=E6=88=B7=E4=B8=AD?= =?UTF-8?q?=E5=BF=83=E5=88=B7=E6=96=B0=E6=8E=89=E7=BA=BF)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 用户中心用 header token 方案:access token 只存内存、refresh token 存 localStorage,页面刷新后靠 POST /v1/auth/refresh 带 X-Refresh-Token 头静默续期 (web/usercenter/lib/api/http.ts)。 但 CORS Allow-Headers 只放行 Content-Type/Authorization,浏览器预检发现 X-Refresh-Token 不在白名单 → 拦截真正的 refresh 请求 → api.refresh() 抛网络错 → setAuthed(false) → 甩回登录页。表现:登录能成功(不带该头),但一刷新就掉线, 用量/续费等需登录态的页面全打不开。 修:Allow-Headers 加 X-Refresh-Token。加回归断言(cors_test)。 Co-Authored-By: Claude Opus 4.8 --- server/internal/httpapi/cors.go | 5 ++++- server/internal/httpapi/cors_test.go | 6 ++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/server/internal/httpapi/cors.go b/server/internal/httpapi/cors.go index 752f046..56f4bac 100644 --- a/server/internal/httpapi/cors.go +++ b/server/internal/httpapi/cors.go @@ -35,7 +35,10 @@ func NewCORS() func(http.Handler) http.Handler { h.Set("Access-Control-Allow-Origin", origin) h.Add("Vary", "Origin") h.Set("Access-Control-Allow-Methods", "GET, POST, PUT, PATCH, DELETE, OPTIONS") - h.Set("Access-Control-Allow-Headers", "Content-Type, Authorization") + // X-Refresh-Token:静默续期/登出把 refresh token 放在此自定义头里 + // (header token 方案,见 web/usercenter/lib/api/http.ts)。不放行则浏览器 + // 预检拦截 /v1/auth/refresh → 登录后一刷新即掉线(login 不带此头故不受影响)。 + h.Set("Access-Control-Allow-Headers", "Content-Type, Authorization, X-Refresh-Token") h.Set("Access-Control-Max-Age", "600") } // 预检请求直接 204(不落到业务路由,避免 /v1/... 的 OPTIONS 404)。 diff --git a/server/internal/httpapi/cors_test.go b/server/internal/httpapi/cors_test.go index 78727f0..390f3c0 100644 --- a/server/internal/httpapi/cors_test.go +++ b/server/internal/httpapi/cors_test.go @@ -3,6 +3,7 @@ package httpapi import ( "net/http" "net/http/httptest" + "strings" "testing" ) @@ -35,6 +36,11 @@ func TestCORS(t *testing.T) { if w.Header().Get("Access-Control-Allow-Methods") == "" { t.Fatalf("preflight missing Allow-Methods") } + // 静默续期/登出把 refresh token 放 X-Refresh-Token 头,必须在允许头里, + // 否则浏览器预检拦截 /v1/auth/refresh → 登录后一刷新即掉线。 + if ah := w.Header().Get("Access-Control-Allow-Headers"); !strings.Contains(ah, "X-Refresh-Token") { + t.Fatalf("preflight Allow-Headers must include X-Refresh-Token, got %q", ah) + } // 未白名单 Origin:不补 Allow-Origin。 r = httptest.NewRequest("POST", "/v1/auth/login", nil)