115 lines
2.7 KiB
Go
115 lines
2.7 KiB
Go
package services
|
|
|
|
import (
|
|
"errors"
|
|
"time"
|
|
|
|
"github.com/golang-jwt/jwt/v4"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
type TokenService struct {
|
|
db *gorm.DB
|
|
keyManager *KeyManager
|
|
}
|
|
|
|
type TokenInfo struct {
|
|
Active bool `json:"active"`
|
|
Scope string `json:"scope,omitempty"`
|
|
ClientID string `json:"client_id,omitempty"`
|
|
Username string `json:"username,omitempty"`
|
|
TokenType string `json:"token_type,omitempty"`
|
|
Exp int64 `json:"exp,omitempty"`
|
|
Iat int64 `json:"iat,omitempty"`
|
|
Nbf int64 `json:"nbf,omitempty"`
|
|
Sub string `json:"sub,omitempty"`
|
|
Aud string `json:"aud,omitempty"`
|
|
Iss string `json:"iss,omitempty"`
|
|
Jti string `json:"jti,omitempty"`
|
|
}
|
|
|
|
type RevokedToken struct {
|
|
gorm.Model
|
|
Token string `gorm:"uniqueIndex;not null"`
|
|
ExpiresAt time.Time `gorm:"not null"`
|
|
}
|
|
|
|
func NewTokenService(db *gorm.DB, keyManager *KeyManager) *TokenService {
|
|
return &TokenService{
|
|
db: db,
|
|
keyManager: keyManager,
|
|
}
|
|
}
|
|
|
|
func (s *TokenService) RevokeToken(token, tokenTypeHint string) error {
|
|
// 验证令牌
|
|
claims, err := s.parseToken(token)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// 保存到撤销列表
|
|
revokedToken := &RevokedToken{
|
|
Token: token,
|
|
ExpiresAt: time.Unix(claims["exp"].(int64), 0),
|
|
}
|
|
|
|
return s.db.Create(revokedToken).Error
|
|
}
|
|
|
|
func (s *TokenService) IntrospectToken(token, tokenTypeHint string) (*TokenInfo, error) {
|
|
// 检查令牌是否被撤销
|
|
var revokedToken RevokedToken
|
|
if err := s.db.Where("token = ?", token).First(&revokedToken).Error; err == nil {
|
|
return &TokenInfo{Active: false}, nil
|
|
}
|
|
|
|
// 解析令牌
|
|
claims, err := s.parseToken(token)
|
|
if err != nil {
|
|
return &TokenInfo{Active: false}, nil
|
|
}
|
|
|
|
// 检查令牌是否过期
|
|
exp := time.Unix(claims["exp"].(int64), 0)
|
|
if time.Now().After(exp) {
|
|
return &TokenInfo{Active: false}, nil
|
|
}
|
|
|
|
// 构建令牌信息
|
|
info := &TokenInfo{
|
|
Active: true,
|
|
Scope: claims["scope"].(string),
|
|
ClientID: claims["iss"].(string),
|
|
TokenType: "Bearer",
|
|
Exp: claims["exp"].(int64),
|
|
Iat: claims["iat"].(int64),
|
|
Sub: claims["sub"].(string),
|
|
}
|
|
|
|
return info, nil
|
|
}
|
|
|
|
func (s *TokenService) parseToken(token string) (jwt.MapClaims, error) {
|
|
parsedToken, err := jwt.Parse(token, func(token *jwt.Token) (interface{}, error) {
|
|
if _, ok := token.Method.(*jwt.SigningMethodRSA); !ok {
|
|
return nil, errors.New("unexpected signing method")
|
|
}
|
|
return s.keyManager.GetPrivateKey().Public(), nil
|
|
})
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if claims, ok := parsedToken.Claims.(jwt.MapClaims); ok && parsedToken.Valid {
|
|
return claims, nil
|
|
}
|
|
|
|
return nil, errors.New("invalid token")
|
|
}
|
|
|
|
func (s *TokenService) AutoMigrate() error {
|
|
return s.db.AutoMigrate(&RevokedToken{})
|
|
}
|