81 lines
2.3 KiB
Go
81 lines
2.3 KiB
Go
|
|
// Package middleware 提供 Gin 中间件:跨域、JWT 鉴权、超管校验
|
|||
|
|
package middleware
|
|||
|
|
|
|||
|
|
import (
|
|||
|
|
"strings"
|
|||
|
|
|
|||
|
|
"github.com/gin-gonic/gin"
|
|||
|
|
|
|||
|
|
"nl-game-api-gin/internal/database"
|
|||
|
|
"nl-game-api-gin/internal/model"
|
|||
|
|
"nl-game-api-gin/pkg/jwtutil"
|
|||
|
|
"nl-game-api-gin/pkg/resp"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
// CORS 跨域中间件:开发时前端(5173)与后端(8080)端口不同,需要放行跨域请求
|
|||
|
|
func CORS() gin.HandlerFunc {
|
|||
|
|
return func(c *gin.Context) {
|
|||
|
|
origin := c.GetHeader("Origin")
|
|||
|
|
if origin != "" {
|
|||
|
|
c.Header("Access-Control-Allow-Origin", origin)
|
|||
|
|
c.Header("Access-Control-Allow-Credentials", "true")
|
|||
|
|
c.Header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
|
|||
|
|
c.Header("Access-Control-Allow-Headers", "Content-Type, Authorization")
|
|||
|
|
}
|
|||
|
|
// 预检请求直接返回 204
|
|||
|
|
if c.Request.Method == "OPTIONS" {
|
|||
|
|
c.AbortWithStatus(204)
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
c.Next()
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Auth JWT 鉴权中间件:从 Authorization: Bearer xxx 中解析用户身份
|
|||
|
|
// 校验通过后将 user_id 与 role 写入上下文供后续处理函数使用
|
|||
|
|
func Auth() gin.HandlerFunc {
|
|||
|
|
return func(c *gin.Context) {
|
|||
|
|
authHeader := c.GetHeader("Authorization")
|
|||
|
|
token := strings.TrimPrefix(authHeader, "Bearer ")
|
|||
|
|
if token == "" || token == authHeader {
|
|||
|
|
resp.Unauthorized(c, "请先登录")
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
claims, err := jwtutil.Parse(token)
|
|||
|
|
if err != nil {
|
|||
|
|
resp.Unauthorized(c, "登录已过期,请重新登录")
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
// 查库校验账号仍然有效:否则封禁/删除的用户在 Token 过期前仍可操作全部接口
|
|||
|
|
var user model.User
|
|||
|
|
if err := database.DB.Select("status").First(&user, claims.UserID).Error; err != nil {
|
|||
|
|
resp.Unauthorized(c, "账号不存在,请重新登录")
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
if user.Status == model.UserStatusBanned {
|
|||
|
|
resp.Forbidden(c, "账号已被禁用")
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
// 身份信息写入请求上下文
|
|||
|
|
c.Set("user_id", claims.UserID)
|
|||
|
|
c.Set("role", claims.Role)
|
|||
|
|
c.Next()
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// AdminOnly 超管校验中间件:需在 Auth 之后使用,非超管直接拒绝
|
|||
|
|
func AdminOnly() gin.HandlerFunc {
|
|||
|
|
return func(c *gin.Context) {
|
|||
|
|
if c.GetInt("role") != model.RoleAdmin {
|
|||
|
|
resp.Forbidden(c, "仅超级管理员可操作")
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
c.Next()
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// UID 从上下文取当前登录用户ID的快捷方法
|
|||
|
|
func UID(c *gin.Context) int {
|
|||
|
|
return c.GetInt("user_id")
|
|||
|
|
}
|