package common import ( "time" "github.com/Xhofe/go-cache" "github.com/alist-org/alist/v3/internal/conf" "github.com/alist-org/alist/v3/internal/model" "github.com/golang-jwt/jwt/v4" "github.com/pkg/errors" ) var SecretKey []byte type UserClaims struct { Username string `json:"username"` PwdTS int64 `json:"pwd_ts"` jwt.RegisteredClaims } var validTokenCache = cache.NewMemCache[bool]() func GenerateToken(user *model.User) (tokenString string, err error) { claim := UserClaims{ Username: user.Username, PwdTS: user.PwdTS, RegisteredClaims: jwt.RegisteredClaims{ ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Duration(conf.Conf.TokenExpiresIn) * time.Hour)), IssuedAt: jwt.NewNumericDate(time.Now()), NotBefore: jwt.NewNumericDate(time.Now()), }} token := jwt.NewWithClaims(jwt.SigningMethodHS256, claim) tokenString, err = token.SignedString(SecretKey) if err != nil { return "", err } validTokenCache.Set(tokenString, true) return tokenString, err } func ParseToken(tokenString string) (*UserClaims, error) { token, err := jwt.ParseWithClaims(tokenString, &UserClaims{}, func(token *jwt.Token) (interface{}, error) { return SecretKey, nil }) if IsTokenInvalidated(tokenString) { return nil, errors.New("token is invalidated") } if err != nil { if ve, ok := err.(*jwt.ValidationError); ok { if ve.Errors&jwt.ValidationErrorMalformed != 0 { return nil, errors.New("that's not even a token") } else if ve.Errors&jwt.ValidationErrorExpired != 0 { return nil, errors.New("token is expired") } else if ve.Errors&jwt.ValidationErrorNotValidYet != 0 { return nil, errors.New("token not active yet") } else { return nil, errors.New("couldn't handle this token") } } } if claims, ok := token.Claims.(*UserClaims); ok && token.Valid { return claims, nil } return nil, errors.New("couldn't handle this token") } func InvalidateToken(tokenString string) error { if tokenString == "" { return nil // don't invalidate empty guest token } validTokenCache.Del(tokenString) return nil } func IsTokenInvalidated(tokenString string) bool { _, ok := validTokenCache.Get(tokenString) return !ok }