package handler import ( "net/http" "os" "sync" "time" "github.com/gin-gonic/gin" "github.com/golang-jwt/jwt/v5" ) var jwtSecret []byte func SetJWTSecret(secret string) { jwtSecret = []byte(secret) } func GenerateToken() (string, error) { claims := jwt.MapClaims{ "role": "admin", "exp": time.Now().Add(24 * time.Hour).Unix(), "iat": time.Now().Unix(), } token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) return token.SignedString(jwtSecret) } func AuthMiddleware() gin.HandlerFunc { return func(c *gin.Context) { tokenStr, err := c.Cookie("token") if err != nil || tokenStr == "" { c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "未登录"}) return } token, err := jwt.Parse(tokenStr, func(t *jwt.Token) (interface{}, error) { return jwtSecret, nil }) if err != nil || !token.Valid { c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "令牌无效或已过期"}) return } c.Next() } } // Rate limiter for login endpoint: 5 attempts per minute per IP type rateLimiter struct { mu sync.Mutex attempts map[string][]time.Time } var loginLimiter = &rateLimiter{attempts: make(map[string][]time.Time)} func LoginRateLimitMiddleware() gin.HandlerFunc { return func(c *gin.Context) { ip := c.ClientIP() loginLimiter.mu.Lock() defer loginLimiter.mu.Unlock() now := time.Now() window := now.Add(-1 * time.Minute) // Prune old entries valid := make([]time.Time, 0) for _, t := range loginLimiter.attempts[ip] { if t.After(window) { valid = append(valid, t) } } loginLimiter.attempts[ip] = valid if len(valid) >= 5 { c.AbortWithStatusJSON(http.StatusTooManyRequests, gin.H{"error": "登录尝试过于频繁,请稍后再试"}) return } loginLimiter.attempts[ip] = append(loginLimiter.attempts[ip], now) c.Next() } } func IsSecure() bool { return os.Getenv("GIN_MODE") == "release" }