package session import ( "context" "encoding/json" "errors" "time" "giter.top/smart/pkg/config" "giter.top/smart/pkg/security" "github.com/redis/go-redis/v9" ) // ErrInvalidSession 会话不存在或已过期。 var ErrInvalidSession = errors.New("session: invalid or expired") const redisKeyPrefix = "auth:sess:" type payload struct { UserID string `json:"user_id"` TenantID string `json:"tenant_id"` } // Store Redis 会话(供 OAuth authorize 与登出)。 type Store struct { rdb redis.UniversalClient cfg *config.Config } // NewStore 创建会话存储。 func NewStore(rdb redis.UniversalClient, cfg *config.Config) *Store { return &Store{rdb: rdb, cfg: cfg} } func (s *Store) ttl() time.Duration { t := s.cfg.Auth.Session.TTL if t == 0 { return 168 * time.Hour } return t } // Create 创建会话并返回 session id(写入 Cookie 用)。 func (s *Store) Create(ctx context.Context, userID, tenantID string) (sid string, err error) { sid, err = security.RandomURLSafe(32) if err != nil { return "", err } b, err := json.Marshal(payload{UserID: userID, TenantID: tenantID}) if err != nil { return "", err } return sid, s.rdb.Set(ctx, redisKeyPrefix+sid, b, s.ttl()).Err() } // Get 解析会话。 func (s *Store) Get(ctx context.Context, sid string) (userID, tenantID string, err error) { b, err := s.rdb.Get(ctx, redisKeyPrefix+sid).Bytes() if err == redis.Nil { return "", "", ErrInvalidSession } if err != nil { return "", "", err } var p payload if err := json.Unmarshal(b, &p); err != nil { return "", "", ErrInvalidSession } return p.UserID, p.TenantID, nil } // Delete 登出时删除。 func (s *Store) Delete(ctx context.Context, sid string) error { return s.rdb.Del(ctx, redisKeyPrefix+sid).Err() }