cronsun/web/session/session.go

151 lines
3.1 KiB
Go

package session
import (
"bytes"
"encoding/gob"
"net/http"
client "github.com/coreos/etcd/clientv3"
"github.com/shunfei/cronsun"
"github.com/shunfei/cronsun/conf"
"github.com/shunfei/cronsun/log"
"github.com/shunfei/cronsun/utils"
)
func init() {
gob.Register(cronsun.Administrator)
gob.Register(cronsun.Developer)
}
var Manager SessionManager
var cookieCharacters = []byte("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
type SessionManager interface {
Get(w http.ResponseWriter, r *http.Request) (*Session, error)
Store(*Session) error
Destroy(w http.ResponseWriter, r *http.Request)
CleanSeesionData(id string)
}
type storeData struct {
leaseID client.LeaseID
Email string
Data map[interface{}]interface{}
}
type Session struct {
m SessionManager
key string
storeData
}
func (s *Session) ID() string {
return s.key
}
func (s *Session) Store() error {
err := s.m.Store(s)
if err != nil {
log.Errorf("Failed to store session[%s]: %s", s.key, err.Error())
}
return err
}
type EtcdStore struct {
client *cronsun.Client
conf conf.SessionConfig
}
func NewEtcdStore(cli *cronsun.Client, conf conf.SessionConfig) *EtcdStore {
return &EtcdStore{
client: cli,
conf: conf,
}
}
func (this *EtcdStore) Get(w http.ResponseWriter, r *http.Request) (sess *Session, err error) {
c, err := r.Cookie(this.conf.CookieName)
if err != nil && err != http.ErrNoCookie {
log.Infof("get cookie err: %s", err.Error())
} else {
err = nil
}
sess = &Session{
m: this,
storeData: storeData{
Data: make(map[interface{}]interface{}, 2),
},
}
if c == nil {
sess.key = utils.RandString(32, cookieCharacters...)
c = &http.Cookie{
Name: this.conf.CookieName,
Value: sess.key,
Path: "/",
HttpOnly: true,
Secure: false,
MaxAge: this.conf.Expiration,
}
http.SetCookie(w, c)
r.AddCookie(c)
return
}
sess.key = c.Value
resp, err := this.client.Get(this.storeKey(c.Value))
if err != nil {
return
}
if len(resp.Kvs) == 0 {
return sess, nil
}
var buffer = bytes.NewBuffer(resp.Kvs[0].Value)
dec := gob.NewDecoder(buffer)
err = dec.Decode(&sess.storeData)
return
}
func (this *EtcdStore) Store(sess *Session) (err error) {
var buffer = bytes.NewBuffer(nil)
enc := gob.NewEncoder(buffer)
err = enc.Encode(sess.storeData)
if err != nil {
return
}
if sess.leaseID == 0 {
lresp, err := this.client.Grant(int64(this.conf.Expiration))
if err != nil {
return err
}
sess.leaseID = lresp.ID
}
_, err = this.client.Put(this.storeKey(sess.key), buffer.String(), client.WithLease(sess.leaseID))
this.client.KeepAliveOnce(sess.leaseID)
return
}
func (this *EtcdStore) Destroy(w http.ResponseWriter, r *http.Request) {
c, err := r.Cookie(this.conf.CookieName)
if err != nil || c == nil {
return
}
this.CleanSeesionData(c.Value)
}
func (this *EtcdStore) CleanSeesionData(id string) {
_, err := this.client.Delete(this.storeKey(id))
if err != nil {
log.Errorf("Failed to remove session [%s] from etcd: %s", this.storeKey(id), err.Error())
}
}
func (this *EtcdStore) storeKey(key string) string {
return this.conf.StorePrefixPath + key
}