开源前加固认证与部署契约:加密随机 JWT、改密事务吊销、登录 CSRF,并禁止生产回落 localhost。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-09-23 02:07:34 +08:00
parent 6f054a903c
commit 3b550f2124
24 changed files with 372 additions and 355 deletions

View File

@@ -59,27 +59,46 @@ func setAuthCookies(c *gin.Context, accessToken, refreshToken string, secure boo
})
}
// clearAuthCookies 清除当前契约下的三枚认证 cookie(名称随 ConfigureCookieNames,Path=/)。
// clearAuthCookies 清除当前契约下的认证 cookie,并顺带清历史 Path=/api/auth 与无前缀名。
func clearAuthCookies(c *gin.Context, secure bool) {
cookies := []struct {
type ck struct {
name string
httpOnly bool
}{
}
cookies := []ck{
{service.CookieName, true},
{service.RefreshCookieName, true},
{service.CSRFCookieName, false},
// 历史无 __Host- 前缀(切换 DEV_MODE 后避免双 cookie)
{"j13_token", true},
{"j13_refresh", true},
{"j13_csrf", false},
{"__Host-j13_token", true},
{"__Host-j13_refresh", true},
{"__Host-j13_csrf", false},
}
paths := []string{"/", "/api/auth"}
for _, ck := range cookies {
http.SetCookie(c.Writer, &http.Cookie{
Name: ck.name,
Value: "",
Path: "/",
MaxAge: -1,
HttpOnly: ck.httpOnly,
Secure: secure,
SameSite: http.SameSiteLaxMode,
Expires: time.Unix(0, 0),
})
for _, path := range paths {
// __Host- 要求 Secure=true 且 Path=/;非 / 路径跳过 Host 前缀名
if strings.HasPrefix(ck.name, "__Host-") && path != "/" {
continue
}
sec := secure
if strings.HasPrefix(ck.name, "__Host-") {
sec = true
}
http.SetCookie(c.Writer, &http.Cookie{
Name: ck.name,
Value: "",
Path: path,
MaxAge: -1,
HttpOnly: ck.httpOnly,
Secure: sec,
SameSite: http.SameSiteLaxMode,
Expires: time.Unix(0, 0),
})
}
}
}
@@ -233,7 +252,7 @@ func (h *Handlers) Refresh(c *gin.Context) {
// access JWT 无状态、15 分钟自然过期;登出后 tv 不递增,属可接受的短窗口。
func (h *Handlers) Logout(c *gin.Context) {
if refreshToken, err := c.Cookie(service.RefreshCookieName); err == nil && refreshToken != "" {
h.Auth.RevokeRefreshToken(refreshToken)
_ = h.Auth.RevokeRefreshToken(refreshToken)
}
clearAuthCookies(c, !h.Cfg.DevMode)
c.JSON(http.StatusOK, gin.H{"message": "已登出"})