77 lines
1.7 KiB
Go
77 lines
1.7 KiB
Go
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()
|
|
}
|