功能更新
This commit is contained in:
125
internal/middleware/admin.go
Normal file
125
internal/middleware/admin.go
Normal file
@@ -0,0 +1,125 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"gorm.io/gorm"
|
||||
|
||||
"nl-pms-api/internal/commonservice"
|
||||
"nl-pms-api/internal/model"
|
||||
)
|
||||
|
||||
const (
|
||||
HeaderAdminStepUp = "X-Admin-StepUp"
|
||||
CtxClientIP = "client_ip"
|
||||
)
|
||||
|
||||
// AttachClientIP 把解析后的客户端 IP 写入上下文。
|
||||
func AttachClientIP() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
c.Set(CtxClientIP, strings.TrimSpace(c.ClientIP()))
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
||||
// ClientIP 读取中间件写入的客户端 IP。
|
||||
func ClientIP(c *gin.Context) string {
|
||||
v, _ := c.Get(CtxClientIP)
|
||||
s, _ := v.(string)
|
||||
if s == "" {
|
||||
return strings.TrimSpace(c.ClientIP())
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// RejectDisabled 拒绝已禁用账号(在 JWT 之后)。
|
||||
func RejectDisabled(db *gorm.DB) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
uid := commonservice.UserID(c)
|
||||
if uid <= 0 {
|
||||
c.Next()
|
||||
return
|
||||
}
|
||||
var u model.User
|
||||
if err := db.Select("id", "disabled").First(&u, uid).Error; err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "UNAUTHORIZED"})
|
||||
return
|
||||
}
|
||||
if u.Disabled != 0 {
|
||||
c.AbortWithStatusJSON(http.StatusForbidden, gin.H{"error": "ACCOUNT_DISABLED"})
|
||||
return
|
||||
}
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
||||
// RequireAdmin 仅允许 users.id=1。
|
||||
func RequireAdmin() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
if commonservice.UserID(c) != commonservice.AdminUserID {
|
||||
c.AbortWithStatusJSON(http.StatusForbidden, gin.H{"error": "FORBIDDEN"})
|
||||
return
|
||||
}
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
||||
// RequireAdminStepUp 校验 2 小时内有效的动态码二次验证,且 IP 与签发时一致。
|
||||
func RequireAdminStepUp(db *gorm.DB) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
if commonservice.UserID(c) != commonservice.AdminUserID {
|
||||
c.AbortWithStatusJSON(http.StatusForbidden, gin.H{"error": "FORBIDDEN"})
|
||||
return
|
||||
}
|
||||
token := strings.TrimSpace(c.GetHeader(HeaderAdminStepUp))
|
||||
if token == "" {
|
||||
c.AbortWithStatusJSON(http.StatusForbidden, gin.H{"error": "ADMIN_STEPUP_REQUIRED"})
|
||||
return
|
||||
}
|
||||
ip := ClientIP(c)
|
||||
var row model.AdminStepup
|
||||
if err := db.First(&row, commonservice.AdminUserID).Error; err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusForbidden, gin.H{"error": "ADMIN_STEPUP_REQUIRED"})
|
||||
return
|
||||
}
|
||||
sum := sha256.Sum256([]byte(token))
|
||||
hash := hex.EncodeToString(sum[:])
|
||||
if row.TokenHash != hash {
|
||||
c.AbortWithStatusJSON(http.StatusForbidden, gin.H{"error": "ADMIN_STEPUP_REQUIRED"})
|
||||
return
|
||||
}
|
||||
exp, err := time.Parse(time.RFC3339, row.ExpiresAt)
|
||||
if err != nil || time.Now().UTC().After(exp) {
|
||||
_ = db.Delete(&model.AdminStepup{}, commonservice.AdminUserID).Error
|
||||
c.AbortWithStatusJSON(http.StatusForbidden, gin.H{"error": "ADMIN_STEPUP_REQUIRED"})
|
||||
return
|
||||
}
|
||||
if row.ClientIP != "" && ip != "" && row.ClientIP != ip {
|
||||
_ = db.Delete(&model.AdminStepup{}, commonservice.AdminUserID).Error
|
||||
c.AbortWithStatusJSON(http.StatusForbidden, gin.H{
|
||||
"error": "ADMIN_IP_CHANGED",
|
||||
"message": "检测到 IP 变化,存在账号被盗风险,请重新输入动态码",
|
||||
})
|
||||
return
|
||||
}
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
||||
// RequireStepUpForGlobalSetting 仅当写入全局设置键时要求 stepup。
|
||||
func RequireStepUpForGlobalSetting(db *gorm.DB) gin.HandlerFunc {
|
||||
inner := RequireAdminStepUp(db)
|
||||
return func(c *gin.Context) {
|
||||
name := strings.TrimSpace(c.Param("name"))
|
||||
if name == "file_storage" || strings.HasPrefix(name, "fest_img:") {
|
||||
inner(c)
|
||||
return
|
||||
}
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user