Files
backend/auth.go
T
NanamiAdmin d49090a2ab refactor(auth): extract timestamp validation to separate function
Move timestamp validation logic from handlers to ValidateTimeStamp function in auth.go to avoid code duplication and improve maintainability
2026-02-27 21:21:18 +08:00

179 lines
3.5 KiB
Go

package main
import (
"crypto/rand"
"crypto/sha256"
"encoding/base64"
"encoding/hex"
"errors"
"fmt"
"strings"
"sync"
"time"
)
type TokenInfo struct {
Token string
CreatedAt time.Time
UserID int
}
var (
tokenMap = make(map[int]*TokenInfo)
tokenMux sync.RWMutex
tokenTTL = time.Hour
)
func GenerateToken(userID int) (string, error) {
randomBytes := make([]byte, 32)
_, err := rand.Read(randomBytes)
if err != nil {
return "", fmt.Errorf("failed to generate random bytes: %w", err)
}
hash := sha256.Sum256(append(randomBytes, []byte(fmt.Sprintf("%d", userID))...))
token := base64.URLEncoding.EncodeToString(hash[:])
tokenMux.Lock()
defer tokenMux.Unlock()
tokenMap[userID] = &TokenInfo{
Token: token,
CreatedAt: time.Now(),
UserID: userID,
}
return token, nil
}
func ValidateToken(userID int, token string) error {
tokenMux.RLock()
defer tokenMux.RUnlock()
tokenInfo, exists := tokenMap[userID]
if !exists {
return errors.New("token not found")
}
if tokenInfo.Token != token {
return errors.New("invalid token")
}
if time.Since(tokenInfo.CreatedAt) > tokenTTL {
return errors.New("token expired")
}
return nil
}
func RefreshToken(userID int) (string, error) {
tokenMux.Lock()
defer tokenMux.Unlock()
randomBytes := make([]byte, 32)
_, err := rand.Read(randomBytes)
if err != nil {
return "", fmt.Errorf("failed to generate random bytes: %w", err)
}
hash := sha256.Sum256(append(randomBytes, []byte(fmt.Sprintf("%d", userID))...))
token := base64.URLEncoding.EncodeToString(hash[:])
tokenMap[userID] = &TokenInfo{
Token: token,
CreatedAt: time.Now(),
UserID: userID,
}
return token, nil
}
func RemoveToken(userID int) {
tokenMux.Lock()
defer tokenMux.Unlock()
delete(tokenMap, userID)
}
func GetTokenInfo(userID int) (*TokenInfo, error) {
tokenMux.RLock()
defer tokenMux.RUnlock()
tokenInfo, exists := tokenMap[userID]
if !exists {
return nil, errors.New("token not found")
}
return tokenInfo, nil
}
func extractUserIDFromToken(token string) (int, error) {
tokenMux.RLock()
defer tokenMux.RUnlock()
for userID, tokenInfo := range tokenMap {
if tokenInfo.Token == token {
return userID, nil
}
}
return 0, errors.New("invalid token")
}
func CleanupExpiredTokens() {
tokenMux.Lock()
defer tokenMux.Unlock()
for userID, tokenInfo := range tokenMap {
if time.Since(tokenInfo.CreatedAt) > tokenTTL {
delete(tokenMap, userID)
}
}
}
func hashPassword(password string) (string, error) {
hash := sha256.Sum256([]byte(password))
return hex.EncodeToString(hash[:]), nil
}
func verifyPassword(password, hashedPassword string) bool {
hash, err := hashPassword(password)
if err != nil {
return false
}
return hash == hashedPassword
}
func isValidPassword(password string) bool {
if len(password) < 8 {
return false
}
hasUpper := false
hasLower := false
hasDigit := false
hasSpecial := false
specialChars := "!@#$%^&*()_+-=[]{}|;:,.<>?"
for _, char := range password {
switch {
case char >= 'A' && char <= 'Z':
hasUpper = true
case char >= 'a' && char <= 'z':
hasLower = true
case char >= '0' && char <= '9':
hasDigit = true
case strings.ContainsRune(specialChars, char):
hasSpecial = true
}
}
return hasUpper && hasLower && hasDigit && hasSpecial
}
func ValidateTimeStamp(timeStamp int64) error {
currentTime := time.Now().UnixMilli()
if !globalConfig.Debug && (currentTime-timeStamp > 3000 || timeStamp-currentTime > 3000) {
return errors.New("timestamp out of valid range")
}
return nil
}