开源前加固认证与部署契约:加密随机 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": "已登出"})

View File

@@ -13,7 +13,6 @@ func (h *Handlers) Health(c *gin.Context) {
if err := model.PingDB(); err != nil {
c.JSON(http.StatusServiceUnavailable, gin.H{
"status": "error",
"error": err.Error(),
"version": version.Version,
})
return

View File

@@ -538,10 +538,11 @@ func (h *Handlers) ToggleRecommend(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"recommended": rec})
}
// requireAdminOrAbove 置顶/加精仅管理员及以上可用;校验失败已写响应,返回 false
// requireAdminOrAbove 置顶/加精仅管理员及以上可用;以 DB Actor 为准
func (h *Handlers) requireAdminOrAbove(c *gin.Context) bool {
claims := middleware.CurrentUser(c)
if model.RoleLevel(model.Role(claims.Role)) < model.RoleLevel(model.RoleAdmin) {
actor, err := h.Auth.LoadActor(claims.ID)
if err != nil || model.RoleLevel(actor.Role) < model.RoleLevel(model.RoleAdmin) {
c.JSON(http.StatusForbidden, gin.H{"error": "仅管理员可操作"})
return false
}