Files
backend/internal/model/session.go

55 lines
1.9 KiB
Go
Raw Normal View History

package model
import (
"time"
"gorm.io/gorm"
)
// Session 用户登录会话
//
// 用于支持令牌撤销:
// - access token 携带 SessionIDJWT sid中间件校验会话是否仍有效。
// - refresh token 同样携带 SessionID刷新时轮换并校验。
// - 登出/封禁时撤销会话,旧 token 在 access TTL 内可见但 refresh 立即失效。
//
// RefreshTokenHash 存储 refresh token 的 SHA256 哈希,便于按 refresh token 反查会话,
// 不直接存储 token 明文以降低泄露风险。
type Session struct {
ID string `json:"id" gorm:"type:varchar(36);primaryKey"`
UserID string `json:"user_id" gorm:"type:varchar(36);index;not null"`
RefreshTokenHash string `json:"-" gorm:"type:varchar(64);uniqueIndex;not null"`
UserAgent string `json:"user_agent" gorm:"type:varchar(255)"`
IP string `json:"ip" gorm:"type:varchar(45)"`
IssuedAt time.Time `json:"issued_at"`
ExpiresAt time.Time `json:"expires_at" gorm:"index"`
RevokedAt *time.Time `json:"revoked_at,omitempty" gorm:"index"`
LastUsedAt time.Time `json:"last_used_at"`
CreatedAt time.Time `json:"created_at" gorm:"autoCreateTime"`
UpdatedAt time.Time `json:"updated_at" gorm:"autoUpdateTime"`
}
func (Session) TableName() string {
return "sessions"
}
// BeforeCreate 创建前生成 UUID。
func (s *Session) BeforeCreate(tx *gorm.DB) error {
SetUUIDIfEmpty(&s.ID)
return nil
}
// IsRevoked 会话是否已撤销。
func (s *Session) IsRevoked() bool {
return s != nil && s.RevokedAt != nil
}
// IsExpired 会话是否已过期(按 ExpiresAt
func (s *Session) IsExpired(now time.Time) bool {
return s != nil && !s.ExpiresAt.IsZero() && now.After(s.ExpiresAt)
}
// IsValid 会话是否仍有效(未撤销且未过期)。
func (s *Session) IsValid(now time.Time) bool {
return s != nil && !s.IsRevoked() && !s.IsExpired(now)
}