55 lines
1.9 KiB
Go
55 lines
1.9 KiB
Go
|
|
package model
|
|||
|
|
|
|||
|
|
import (
|
|||
|
|
"time"
|
|||
|
|
|
|||
|
|
"gorm.io/gorm"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
// Session 用户登录会话
|
|||
|
|
//
|
|||
|
|
// 用于支持令牌撤销:
|
|||
|
|
// - access token 携带 SessionID(JWT 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)
|
|||
|
|
}
|