Compare commits

...

1 Commits

Author SHA1 Message Date
wangjia 84448b3064 fix(server/cors): 放行 X-Refresh-Token 头(修用户中心刷新掉线)
Deploy Server / deploy-server (push) Successful in 3m3s
用户中心用 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 <noreply@anthropic.com>
2026-07-07 23:03:39 +08:00
2 changed files with 10 additions and 1 deletions
+4 -1
View File
@@ -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)。
+6
View File
@@ -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)