138 lines
3.4 KiB
Go
138 lines
3.4 KiB
Go
package sessions
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"encoding/base64"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/VictoriaMetrics/fastcache"
|
|
"github.com/goccy/go-json"
|
|
)
|
|
|
|
const (
|
|
// Default settings
|
|
DefaultMaxSize = 100 * 1024 * 1024 // 100MB default cache size
|
|
DefaultCookieName = "MSESSID"
|
|
DefaultCookiePath = "/"
|
|
DefaultMaxAge = 86400 // 1 day in seconds
|
|
)
|
|
|
|
// SessionManager handles multiple sessions using fastcache for storage
|
|
type SessionManager struct {
|
|
cache *fastcache.Cache
|
|
cookieName string
|
|
cookiePath string
|
|
cookieDomain string
|
|
cookieSecure bool
|
|
cookieHTTPOnly bool
|
|
cookieMaxAge int
|
|
mu sync.RWMutex // Only for cookie settings
|
|
}
|
|
|
|
// NewSessionManager creates a new session manager with optional cache size
|
|
func NewSessionManager(maxSize ...int) *SessionManager {
|
|
size := DefaultMaxSize
|
|
if len(maxSize) > 0 && maxSize[0] > 0 {
|
|
size = maxSize[0]
|
|
}
|
|
|
|
return &SessionManager{
|
|
cache: fastcache.New(size),
|
|
cookieName: DefaultCookieName,
|
|
cookiePath: DefaultCookiePath,
|
|
cookieHTTPOnly: true,
|
|
cookieMaxAge: DefaultMaxAge,
|
|
}
|
|
}
|
|
|
|
// generateSessionID creates a cryptographically secure random session ID
|
|
func generateSessionID() string {
|
|
b := make([]byte, 32)
|
|
if _, err := rand.Read(b); err != nil {
|
|
return time.Now().String() // Fallback
|
|
}
|
|
return base64.URLEncoding.EncodeToString(b)
|
|
}
|
|
|
|
// GetSession retrieves a session by ID, or creates a new one if it doesn't exist
|
|
func (sm *SessionManager) GetSession(id string) *Session {
|
|
// Check if session exists
|
|
data := sm.cache.Get(nil, []byte(id))
|
|
|
|
if len(data) > 0 {
|
|
// Session exists, unmarshal it
|
|
session := &Session{}
|
|
if err := json.Unmarshal(data, session); err == nil {
|
|
// Update last accessed time
|
|
session.UpdatedAt = time.Now()
|
|
|
|
// Store back with updated timestamp
|
|
updatedData, _ := json.Marshal(session)
|
|
sm.cache.Set([]byte(id), updatedData)
|
|
|
|
return session
|
|
}
|
|
}
|
|
|
|
// Create new session
|
|
session := NewSession(id)
|
|
data, _ = json.Marshal(session)
|
|
sm.cache.Set([]byte(id), data)
|
|
|
|
return session
|
|
}
|
|
|
|
// CreateSession generates a new session with a unique ID
|
|
func (sm *SessionManager) CreateSession() *Session {
|
|
id := generateSessionID()
|
|
|
|
session := NewSession(id)
|
|
data, _ := json.Marshal(session)
|
|
sm.cache.Set([]byte(id), data)
|
|
|
|
return session
|
|
}
|
|
|
|
// SaveSession persists a session back to the cache
|
|
func (sm *SessionManager) SaveSession(session *Session) {
|
|
data, _ := json.Marshal(session)
|
|
sm.cache.Set([]byte(session.ID), data)
|
|
}
|
|
|
|
// DestroySession removes a session
|
|
func (sm *SessionManager) DestroySession(id string) {
|
|
sm.cache.Del([]byte(id))
|
|
}
|
|
|
|
// CookieOptions returns the cookie options for this session manager
|
|
func (sm *SessionManager) CookieOptions() map[string]any {
|
|
sm.mu.RLock()
|
|
defer sm.mu.RUnlock()
|
|
|
|
return map[string]any{
|
|
"name": sm.cookieName,
|
|
"path": sm.cookiePath,
|
|
"domain": sm.cookieDomain,
|
|
"secure": sm.cookieSecure,
|
|
"http_only": sm.cookieHTTPOnly,
|
|
"max_age": sm.cookieMaxAge,
|
|
}
|
|
}
|
|
|
|
// SetCookieOptions configures cookie parameters
|
|
func (sm *SessionManager) SetCookieOptions(name, path, domain string, secure, httpOnly bool, maxAge int) {
|
|
sm.mu.Lock()
|
|
defer sm.mu.Unlock()
|
|
|
|
sm.cookieName = name
|
|
sm.cookiePath = path
|
|
sm.cookieDomain = domain
|
|
sm.cookieSecure = secure
|
|
sm.cookieHTTPOnly = httpOnly
|
|
sm.cookieMaxAge = maxAge
|
|
}
|
|
|
|
// GlobalSessionManager is the default session manager instance
|
|
var GlobalSessionManager = NewSessionManager()
|