106 lines
2.1 KiB
Go
106 lines
2.1 KiB
Go
package session
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"encoding/base64"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
var (
|
|
providers = make(map[string]IProvider)
|
|
session *Manager
|
|
lock sync.Mutex
|
|
)
|
|
|
|
type Manager struct {
|
|
provider IProvider
|
|
cookieName string
|
|
maxLifetime int64
|
|
lock sync.Mutex
|
|
}
|
|
|
|
type IManager interface {
|
|
SessionStart(w http.ResponseWriter, r *http.Request) ISession
|
|
GC()
|
|
}
|
|
|
|
type IProvider interface {
|
|
SessionInit(sid string) (ISession, error)
|
|
SessionRead(sid string) (ISession, error)
|
|
SessionDestroy(sid string) error
|
|
SessionGC(maxLifeTime int64)
|
|
}
|
|
|
|
type ISession interface {
|
|
Set(key, value interface{}) error
|
|
Get(key interface{}) interface{}
|
|
Delete(key interface{}) error
|
|
SessionID() string
|
|
}
|
|
|
|
func NewManager(providerName, cookieName string, maxLifetime int64) (*Manager, error) {
|
|
provider, ok := providers[providerName]
|
|
|
|
if !ok {
|
|
return nil, fmt.Errorf("session: unknown provider %q", providerName)
|
|
}
|
|
|
|
if session == nil {
|
|
lock.Lock()
|
|
defer lock.Unlock()
|
|
session = &Manager{provider: provider, cookieName: cookieName, maxLifetime: maxLifetime}
|
|
}
|
|
|
|
return session, nil
|
|
}
|
|
|
|
func (m *Manager) SessionStart(w http.ResponseWriter, r *http.Request) ISession {
|
|
m.lock.Lock()
|
|
defer m.lock.Unlock()
|
|
cookie, err := r.Cookie(m.cookieName)
|
|
var session ISession
|
|
if err != nil || cookie.Value == "" {
|
|
sid := m.sessionId()
|
|
session, _ = m.provider.SessionInit(sid)
|
|
cookie := http.Cookie{Name: m.cookieName, Value: sid, Path: "/", HttpOnly: true, MaxAge: int(m.maxLifetime)}
|
|
http.SetCookie(w, &cookie)
|
|
} else {
|
|
sid := cookie.Value
|
|
session, _ = m.provider.SessionRead(sid)
|
|
}
|
|
|
|
return session
|
|
}
|
|
|
|
func (m *Manager) GC() {
|
|
m.lock.Lock()
|
|
defer m.lock.Unlock()
|
|
m.provider.SessionGC(m.maxLifetime)
|
|
time.AfterFunc(time.Duration(m.maxLifetime), func() { m.GC() })
|
|
}
|
|
|
|
func (m *Manager) sessionId() string {
|
|
b := make([]byte, 32)
|
|
if _, err := io.ReadFull(rand.Reader, b); err != nil {
|
|
return ""
|
|
}
|
|
|
|
return base64.URLEncoding.EncodeToString(b)
|
|
}
|
|
|
|
func Register(name string, provider IProvider) {
|
|
if providers[name] != nil {
|
|
return
|
|
}
|
|
|
|
if provider == nil {
|
|
panic("session: Register provider is nil")
|
|
}
|
|
|
|
providers[name] = provider
|
|
}
|