Files
jiang13-bbs/backend/middleware/auth.go
freefire 0c2b0d2d6a 首次提交:姜十三论坛
后端 Go+Gin:认证/CSRF/限流、板块、帖子、评论、点赞、通知、用户资料、置顶推荐;前端 Next.js 16:发帖/编辑/删除、搜索、分页、点赞、通知中心、设置;基础设施 docker-compose 与配置模板;添加 .gitignore 与专有许可证(保留所有权利)
2026-09-12 01:11:05 +08:00

101 lines
2.4 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package middleware
import (
"net/http"
"strings"
"github.com/freefire/jiang13-bbs/service"
"github.com/gin-gonic/gin"
)
// AuthMiddleware 认证中间件
type AuthMiddleware struct {
auth *service.AuthService
}
func NewAuthMiddleware(auth *service.AuthService) *AuthMiddleware {
return &AuthMiddleware{auth: auth}
}
// OptionalAuth 可选登录:解析 token,失败不阻断
func (m *AuthMiddleware) OptionalAuth() gin.HandlerFunc {
return func(c *gin.Context) {
user, ok := m.parseToken(c)
if ok {
c.Set("user", user)
}
c.Next()
}
}
// RequireAuth 必须登录
func (m *AuthMiddleware) RequireAuth() gin.HandlerFunc {
return func(c *gin.Context) {
user, ok := m.parseToken(c)
if !ok {
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "未登录"})
return
}
if user.Banned {
c.AbortWithStatusJSON(http.StatusForbidden, gin.H{"error": "账号已被封禁"})
return
}
c.Set("user", user)
c.Next()
}
}
// RequireAdmin 必须管理员
func (m *AuthMiddleware) RequireAdmin() gin.HandlerFunc {
return func(c *gin.Context) {
user, ok := m.parseToken(c)
if !ok {
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "未登录"})
return
}
if user.Role != service.RoleAdmin {
c.AbortWithStatusJSON(http.StatusForbidden, gin.H{"error": "需要管理员权限"})
return
}
c.Set("user", user)
c.Next()
}
}
// parseToken 解析并校验 token:
// 1. 优先从 HttpOnly cookie 读取,回退 Authorization header
// 2. 校验 JWT 签名和过期
// 3. 查 DB 实时校验 token_version 和 banned 状态
func (m *AuthMiddleware) parseToken(c *gin.Context) (*service.UserClaims, bool) {
tokenStr, err := c.Cookie(service.CookieName)
if err != nil || tokenStr == "" {
auth := c.GetHeader("Authorization")
if auth == "" {
return nil, false
}
tokenStr = strings.TrimPrefix(auth, "Bearer ")
if tokenStr == auth {
return nil, false
}
}
claims, err := m.auth.ParseToken(tokenStr)
if err != nil {
return nil, false
}
// 实时校验:token_version 匹配 + 未封禁(防止旧 JWT 在封禁/改密码后仍有效)
if _, err := m.auth.ValidateClaims(claims); err != nil {
return nil, false
}
return claims, true
}
// CurrentUser 从 context 获取当前用户
func CurrentUser(c *gin.Context) *service.UserClaims {
if v, ok := c.Get("user"); ok {
if u, ok := v.(*service.UserClaims); ok {
return u
}
}
return nil
}