加固后台管理安全

This commit is contained in:
yml2213
2026-06-11 07:23:00 +08:00
parent 5255b21141
commit 88b1df64e7
41 changed files with 1276 additions and 293 deletions
+66 -8
View File
@@ -1,6 +1,8 @@
package middleware
import (
"context"
"net/http"
"strings"
"hfb_sys/backend/internal/modules/auth"
@@ -10,18 +12,34 @@ import (
)
const (
ContextUserID = "user_id"
ContextPhone = "phone"
ContextAdminID = "admin_id"
ContextUsername = "username"
ContextUserID = "user_id"
ContextPhone = "phone"
ContextAdminID = "admin_id"
ContextUsername = "username"
ContextPasswordMustChange = "password_must_change"
AdminAccessCookieName = "hfb_admin_access"
)
func extractToken(c *gin.Context) string {
type AdminTokenContext struct {
Username string
PasswordMustChange bool
}
type AdminTokenValidatorFunc func(ctx context.Context, adminID uint64, tokenVersion int64) (AdminTokenContext, error)
func extractBearerToken(c *gin.Context) string {
header := c.GetHeader("Authorization")
tokenText := strings.TrimSpace(strings.TrimPrefix(header, "Bearer "))
if tokenText != "" && tokenText != header {
return tokenText
}
return ""
}
func extractToken(c *gin.Context) string {
if tokenText := extractBearerToken(c); tokenText != "" {
return tokenText
}
return c.Query("token")
}
@@ -47,9 +65,14 @@ func Auth(jwtManager *auth.JWTManager) gin.HandlerFunc {
}
}
func AdminAuth(jwtManager *auth.JWTManager) gin.HandlerFunc {
func AdminAuth(jwtManager *auth.JWTManager, validate AdminTokenValidatorFunc) gin.HandlerFunc {
return func(c *gin.Context) {
tokenText := extractToken(c)
tokenText := extractBearerToken(c)
if tokenText == "" {
if cookieToken, err := c.Cookie(AdminAccessCookieName); err == nil {
tokenText = strings.TrimSpace(cookieToken)
}
}
if tokenText == "" {
response.Unauthorized(c, "缺少后台访问令牌")
c.Abort()
@@ -62,9 +85,44 @@ func AdminAuth(jwtManager *auth.JWTManager) gin.HandlerFunc {
c.Abort()
return
}
username := claims.Phone
passwordMustChange := false
if validate != nil {
tokenContext, err := validate(c.Request.Context(), claims.UserID, claims.TokenVersion)
if err != nil {
response.Unauthorized(c, "后台访问令牌无效或已过期")
c.Abort()
return
}
username = tokenContext.Username
passwordMustChange = tokenContext.PasswordMustChange
}
c.Set(ContextAdminID, claims.UserID)
c.Set(ContextUsername, claims.Phone)
c.Set(ContextUsername, username)
c.Set(ContextPasswordMustChange, passwordMustChange)
c.Next()
}
}
func RequireAdminPasswordChanged() gin.HandlerFunc {
allowed := map[string]bool{
"/api/admin/me": true,
"/api/admin/auth/logout": true,
"/api/admin/admin-users/me/password": true,
}
return func(c *gin.Context) {
value, ok := c.Get(ContextPasswordMustChange)
if !ok {
c.Next()
return
}
mustChange, ok := value.(bool)
if !ok || !mustChange || allowed[c.FullPath()] {
c.Next()
return
}
response.Error(c, http.StatusForbidden, "password_must_change", "请先修改初始密码")
c.Abort()
}
}