Официальное API SCS: Управление HTTP-сессиями для Go

Менеджер сессий для GO

Использование пользовательских хранилищ сеансов scs.Store определяет интерфейс для пользовательских хранилищ сеансов. Любой объект, реализующий этот интерфейс, может быть установлен как хранилище при настройке сеанса.

Работа с пользовательскими хранилищами сессий

Интерфейсы хранилищ

Базовый интерфейс Store

Определяет контракт для реализации пользовательских хранилищ:

type Store interface {
    // Delete удаляет токен и данные сессии из хранилища
    // Если токен не существует - операция должна завершиться успешно
    Delete(token string) error

    // Find ищет данные по токену сессии
    // Возвращает:
    // - данные (если найдены)
    // - флаг found (найдено/не найдено)
    // - ошибку (только для системных сбоев)
    Find(token string) ([]byte, bool, error)

    // Commit сохраняет или обновляет данные сессии
    Commit(token string, data []byte, expiry time.Time) error
}

Интерфейс IterableStore

Добавляет возможность перебора всех сессий:

type IterableStore interface {
    // All возвращает все активные (не истекшие) сессии
    // В формате map[токен]данные
    All() (map[string][]byte, error)
}

Версии с поддержкой контекста

CtxStore

Расширяет базовый интерфейс с добавлением context.Context:

type CtxStore interface {
    Store
    
    // Версии методов с поддержкой контекста
    DeleteCtx(ctx context.Context, token string) error
    FindCtx(ctx context.Context, token string) ([]byte, bool, error) 
    CommitCtx(ctx context.Context, token string, data []byte, expiry time.Time) error
}

IterableCtxStore

Аналогично для перебора сессий:

type IterableCtxStore interface {
    AllCtx(ctx context.Context) (map[string][]byte, error)
}

Защита от фиксации сессии

Для предотвращения атак фиксации сессии необходимо:

func loginHandler(w http.ResponseWriter, r *http.Request) {
    userID := 123

    // 1. Сначала обновляем токен сессии
    if err := sessionManager.RenewToken(r.Context()); err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }

    // 2. Затем сохраняем данные пользователя
    sessionManager.Put(r.Context(), "userID", userID)
}

Ключевые моменты:

  1. Всегда обновляйте токен при изменении прав доступа
  2. Порядок операций критически важен
  3. Такой же подход применяйте при выходе пользователя

Это гарантирует, что после аутентификации будет создан новый токен сессии, делая недействительными любые ранее перехваченные токены.

Типы данных в SCS

Интерфейс Codec

Добавлен в v2.2.0

type Codec interface {
    // Encode преобразует срок действия и значения сессии в байтовый срез
    Encode(deadline time.Time, values map[string]interface{}) ([]byte, error)
    
    // Decode восстанавливает срок действия и значения сессии из байтового среза
    Decode([]byte) (deadline time.Time, values map[string]interface{}, err error)
}

Интерфейс для кодирования/декодирования данных сессии при работе с хранилищем.

Интерфейс CtxStore

Добавлен в v2.5.0

type CtxStore interface {
    Store
    
    // Версии методов с поддержкой контекста:
    DeleteCtx(ctx context.Context, token string) error
    FindCtx(ctx context.Context, token string) ([]byte, bool, error)
    CommitCtx(ctx context.Context, token string, b []byte, expiry time.Time) error
}

Расширенный интерфейс хранилища с поддержкой context.Context.

Структура GobCodec

Добавлена в v2.3.1

type GobCodec struct{}

Реализация Codec с использованием encoding/gob для сериализации данных.

Методы GobCodec:

// Decode преобразует байты в срок действия и данные сессии
func (GobCodec) Decode(b []byte) (time.Time, map[string]interface{}, error)

// Encode сериализует данные сессии в байтовый срез  
func (GobCodec) Encode(deadline time.Time, values map[string]interface{}) ([]byte, error)

Интерфейс IterableCtxStore

Добавлен в v2.5.0

type IterableCtxStore interface {
    // AllCtx возвращает все активные сессии (с поддержкой контекста)
    AllCtx(ctx context.Context) (map[string][]byte, error)
}

Интерфейс для перечислимых хранилищ с поддержкой context.Context.

Интерфейс IterableStore

Добавлен в v2.5.0

type IterableStore interface {
    // All возвращает все активные (не истекшие) сессии
    All() (map[string][]byte, error)
}

Базовый интерфейс для хранилищ с возможностью перебора сессий.

Ключевые особенности:

  • Codec - абстракция для сериализации данных
  • GobCodec - стандартная реализация через encoding/gob
  • CtxStore добавляет поддержку контекста к базовому хранилищу
  • Интерфейсы Iterable позволяют работать со всеми сессиями

Конфигурация сессий в SCS

Структура SessionCookie

type SessionCookie struct {
    // Имя cookie сессии (не должно содержать спецсимволы)
    // По умолчанию: "session"
    Name string

    // Домен cookie (по умолчанию - текущий домен)
    Domain string

    // HttpOnly флаг (по умолчанию true)
    HttpOnly bool

    // Путь cookie (по умолчанию "/")
    Path string

    // Сохранять cookie после закрытия браузера (по умолчанию true)
    Persist bool

    // Политика SameSite (по умолчанию Lax)
    SameSite http.SameSite

    // Secure флаг (только HTTPS, по умолчанию false)
    Secure bool
}

Настройки cookie для управления сессиями.

Структура SessionManager

Добавлена в v2.1.0

type SessionManager struct {
    // Таймаут бездействия (опционально)
    IdleTimeout time.Duration

    // Максимальное время жизни сессии (по умолчанию 24 часа)
    Lifetime time.Duration

    // Хранилище сессий
    Store Store

    // Настройки cookie
    Cookie SessionCookie

    // Кодек для сериализации данных (по умолчанию GobCodec)
    Codec Codec

    // Обработчик ошибок (по умолчанию HTTP 500)
    ErrorFunc func(http.ResponseWriter, *http.Request, error)

    // Хэшировать токен в хранилище
    HashTokenInStore bool
    // содержит скрытые поля
}

Основные настройки:

  • IdleTimeout: автоматическое завершение неактивных сессий
  • Lifetime: абсолютное время жизни сессии
  • Store: подключение к Redis, PostgreSQL и другим хранилищам
  • Cookie: тонкая настройка параметров безопасности cookie
  • Codec: кастомная сериализация данных
  • ErrorFunc: обработка ошибок на уровне middleware

Рекомендации по безопасности:

  1. Всегда устанавливайте Secure=true в production
  2. Используйте SameSite=Strict для критичных операций
  3. Для API можно отключить HttpOnly и работать через заголовки
  4. Регулируйте Lifetime в соответствии с требованиями приложения

Пример инициализации:

manager := scs.New()
manager.Lifetime = 12 * time.Hour
manager.Cookie.Secure = true
manager.Cookie.SameSite = http.SameSiteStrictMode
manager.Store = redisstore.New(redisPool)

Методы SessionManager

Основные функции

New

Добавлено в v2.1.0

func New() *SessionManager

Создает новый менеджер сессий с настройками по умолчанию. Потокобезопасен.

Clear (устаревший)

func (s *SessionManager) Clear(ctx context.Context) error

Очищает все данные текущей сессии, не затрагивая токен и время жизни. Если данных нет - операция не выполняется.

Управление сессиями

Commit

Добавлено в v2.1.0

func (s *SessionManager) Commit(ctx context.Context) (string, time.Time, error)

Сохраняет данные сессии в хранилище и возвращает:

  • Токен сессии
  • Время истечения
  • Ошибку (если есть)

Обычно используется автоматически middleware LoadAndSave().

Deadline

Добавлено в v2.5.0

func (s *SessionManager) Deadline(ctx context.Context) time.Time

Возвращает абсолютное время истечения сессии. При использовании IdleTimeout сессия может завершиться раньше из-за неактивности.

Destroy

Добавлено в v2.1.0

func (s *SessionManager) Destroy(ctx context.Context) error

Полностью удаляет сессию:

  1. Удаляет данные из хранилища
  2. Помечает сессию как уничтоженную
  3. Новые операции создадут свежую сессию

Работа с данными

Exists

Добавлено в v2.1.0

func (s *SessionManager) Exists(ctx context.Context, key string) bool

Проверяет наличие ключа в данных сессии.

Get

Добавлено в v2.1.0

func (s *SessionManager) Get(ctx context.Context, key string) interface{}

Возвращает значение по ключу как interface{}, требующее приведения типа:

foo, ok := session.Get(ctx, "foo").(string)
if !ok {
    return errors.New("ошибка приведения типа")
}

GetBool

Добавлено в v2.1.0

func (s *SessionManager) GetBool(ctx context.Context, key string) bool

Возвращает значение как bool (по умолчанию false если ключ отсутствует или тип не совпадает).

GetBytes

Добавлено в v2.1.0

func (s *SessionManager) GetBytes(ctx context.Context, key string) []byte

Возвращает значение как []byte (по умолчанию nil).

Методы работы с данными сессии

Получение типизированных значений

GetFloat

func (s *SessionManager) GetFloat(ctx context.Context, key string) float64

Возвращает значение как float64 (0 если ключ отсутствует или тип не совпадает)

GetInt

func (s *SessionManager) GetInt(ctx context.Context, key string) int  

Возвращает значение как int (0 если ключ отсутствует)

GetInt32

func (s *SessionManager) GetInt32(ctx context.Context, key string) int32

Возвращает значение как int32 (0 по умолчанию)

GetInt64

func (s *SessionManager) GetInt64(ctx context.Context, key string) int64

Возвращает значение как int64 (0 по умолчанию)

GetString

func (s *SessionManager) GetString(ctx context.Context, key string) string

Возвращает строковое значение (пустая строка "" если ключ отсутствует)

GetTime

func (s *SessionManager) GetTime(ctx context.Context, key string) time.Time

Возвращает значение времени (time.IsZero() == true если ключ отсутствует)

Управление сессиями

Iterate

func (s *SessionManager) Iterate(ctx context.Context, fn func(context.Context) error) error

Итерируется по всем активным сессиям и выполняет функцию fn для каждой. Требует поддержки итерации в хранилище.

Keys

func (s *SessionManager) Keys(ctx context.Context) []string

Возвращает отсортированный список всех ключей сессии (пустой слайс если данных нет)

Middleware и загрузка данных

Load

func (s *SessionManager) Load(ctx context.Context, token string) (context.Context, error)

Загружает данные сессии по токену и возвращает новый контекст. Создает новую сессию если токен не найден.

LoadAndSave

func (s *SessionManager) LoadAndSave(next http.Handler) http.Handler

Middleware для автоматической загрузки/сохранения сессии и управления cookie.

Пример:

router := http.NewServeMux()
router.HandleFunc("/", handler)

// Обертывание роутера middleware
app := sessionManager.LoadAndSave(router)

http.ListenAndServe(":4000", app)

Методы управления сессиями в SCS

Работа с данными сессии

MergeSession (v2.5.0+)

func (s *SessionManager) MergeSession(ctx context.Context, token string) error

Объединяет данные из другой сессии (полезно при OAuth-редиректах). Для полной замены данных используйте Clear().

Pop-методы

Одноразовое получение данных с удалением ключа:

func (s *SessionManager) Pop(ctx context.Context, key string) interface{}
func (s *SessionManager) PopBool(ctx context.Context, key string) bool
func (s *SessionManager) PopBytes(ctx context.Context, key string) []byte  
func (s *SessionManager) PopFloat(ctx context.Context, key string) float64
func (s *SessionManager) PopInt(ctx context.Context, key string) int
func (s *SessionManager) PopString(ctx context.Context, key string) string
func (s *SessionManager) PopTime(ctx context.Context, key string) time.Time

Пример для flash-сообщений:

// Установка
sessionManager.Put(ctx, "flash", "Сообщение")

// Получение с удалением  
msg := sessionManager.PopString(ctx, "flash")

Основные операции

Put (v2.1.0+)

func (s *SessionManager) Put(ctx context.Context, key string, val interface{})

Добавляет или обновляет значение в сессии. Автоматически помечает сессию как измененную.

Remove (v2.1.0+)

func (s *SessionManager) Remove(ctx context.Context, key string)

Удаляет ключ из сессии. Если ключ не существует - операция игнорируется.

Управление токенами и временем жизни

RenewToken (v2.1.0+)

func (s *SessionManager) RenewToken(ctx context.Context) error

Генерирует новый токен сессии, сохраняя данные. Критично для:

  • Входа/выхода пользователей
  • Изменения прав доступа
  • Защиты от фиксации сессии

RememberMe (v2.5.0+)

func (s *SessionManager) RememberMe(ctx context.Context, val bool)

Управляет постоянством сессии (работает только при Cookie.Persist = false).

SetDeadline (v2.7.0+)

func (s *SessionManager) SetDeadline(ctx context.Context, expire time.Time)

Устанавливает абсолютное время истечения сессии (может быть перекрыто IdleTimeout).

Информационные методы

Status (v2.1.0+)

func (s *SessionManager) Status(ctx context.Context) Status

Возвращает текущее состояние сессии (новые/измененные/уничтоженные).

Token (v2.5.0+)

func (s *SessionManager) Token(ctx context.Context) string

Возвращает токен сессии (пустую строку до коммита).

WriteSessionCookie (v2.3.0+)

func (s *SessionManager) WriteSessionCookie(ctx context.Context, w http.ResponseWriter, token string, expiry time.Time)

Низкоуровневый метод записи cookie (обычно используется через middleware).

Типы данных и интерфейсы SCS

Тип Status

type Status int

Статус представляет состояние данных сессии в течение цикла запроса.

Константы статусов:

const (
    // Unmodified - данные сессии не изменялись
    Unmodified Status = iota
    
    // Modified - данные были изменены
    Modified
    
    // Destroyed - сессия была уничтожена
    Destroyed
)

Использование:

status := sessionManager.Status(ctx)
switch status {
case scs.Unmodified:
    // Сессия не изменялась
case scs.Modified:
    // Требуется сохранение изменений  
case scs.Destroyed:
    // Сессия закрыта
}

Интерфейс Store

type Store interface {
    // Delete удаляет токен и данные сессии
    // Если токен не существует - операция игнорируется
    Delete(token string) error

    // Find ищет данные по токену сессии
    // Возвращает:
    // - данные (если найдены)
    // - флаг существования
    // - ошибку (только для системных сбоев)
    Find(token string) ([]byte, bool, error)

    // Commit сохраняет/обновляет данные сессии
    Commit(token string, data []byte, expiry time.Time) error
}

Базовый интерфейс для реализации хранилищ сессий.

Особенности реализации:

  1. Delete должен быть идемпотентным
  2. Find возвращает found=false для:
    • Несуществующих токенов
    • Истекших сессий
    • Поврежденных данных
  3. Commit должен перезаписывать существующие данные

Пример проверки сессии:

data, found, err := store.Find(token)
if err != nil {
    // Обработка системной ошибки
}
if !found {
    // Сессия не существует/истекла
}

Этот интерфейс позволяет интегрировать SCS с любыми системами хранения данных.