|
|
package SessionUtil
|
|
|
|
|
|
import (
|
|
|
"crypto/rand"
|
|
|
"encoding/base64"
|
|
|
"fmt"
|
|
|
"io"
|
|
|
"net/http"
|
|
|
"net/url"
|
|
|
"sync"
|
|
|
"time"
|
|
|
)
|
|
|
|
|
|
//session存储方式接口
|
|
|
type Provider interface {
|
|
|
//初始化一个session,sid根据需要生成后传入
|
|
|
SessionInit(sid string) (Session, error)
|
|
|
//根据sid,获取session
|
|
|
SessionRead(sid string) (Session, error)
|
|
|
//销毁session
|
|
|
SessionDestroy(sid string) error
|
|
|
//回收
|
|
|
SessionGC(maxLifeTime int64)
|
|
|
}
|
|
|
|
|
|
//Session操作接口
|
|
|
type Session interface {
|
|
|
Set(key, value interface{}) error
|
|
|
Get(key interface{}) interface{}
|
|
|
Delete(ket interface{}) error
|
|
|
SessionID() string
|
|
|
}
|
|
|
|
|
|
type Manager struct {
|
|
|
cookieName string
|
|
|
lock sync.Mutex //互斥锁
|
|
|
provider Provider //存储session方式
|
|
|
maxLifeTime int64 //有效期
|
|
|
}
|
|
|
|
|
|
//实例化一个session管理器
|
|
|
func NewSessionManager(provideName, cookieName string, maxLifeTime int64) (*Manager, error) {
|
|
|
provide, ok := provides[provideName]
|
|
|
if !ok {
|
|
|
return nil, fmt.Errorf("session: unknown provide %q ", provideName)
|
|
|
}
|
|
|
return &Manager{cookieName: cookieName, provider: provide, maxLifeTime: maxLifeTime}, nil
|
|
|
}
|
|
|
|
|
|
//注册 由实现Provider接口的结构体调用
|
|
|
func Register(name string, provide Provider) {
|
|
|
if provide == nil {
|
|
|
panic("session: Register provide is nil")
|
|
|
}
|
|
|
if _, ok := provides[name]; ok {
|
|
|
panic("session: Register called twice for provide " + name)
|
|
|
}
|
|
|
provides[name] = provide
|
|
|
}
|
|
|
|
|
|
var provides = make(map[string]Provider)
|
|
|
|
|
|
//生成sessionId
|
|
|
func (manager *Manager) sessionId() string {
|
|
|
b := make([]byte, 32)
|
|
|
if _, err := io.ReadFull(rand.Reader, b); err != nil {
|
|
|
return ""
|
|
|
}
|
|
|
//加密
|
|
|
return base64.URLEncoding.EncodeToString(b)
|
|
|
}
|
|
|
|
|
|
//判断当前请求的cookie中是否存在有效的session,存在返回,否则创建
|
|
|
func (manager *Manager) SessionStart(w http.ResponseWriter, r *http.Request) (session Session) {
|
|
|
manager.lock.Lock() //加锁
|
|
|
defer manager.lock.Unlock()
|
|
|
cookie, err := r.Cookie(manager.cookieName)
|
|
|
if err != nil || cookie.Value == "" {
|
|
|
//创建一个
|
|
|
sid := manager.sessionId()
|
|
|
session, _ = manager.provider.SessionInit(sid)
|
|
|
cookie := http.Cookie{
|
|
|
Name: manager.cookieName,
|
|
|
Value: url.QueryEscape(sid), //转义特殊符号@#¥%+*-等
|
|
|
Path: "/",
|
|
|
HttpOnly: true,
|
|
|
MaxAge: int(manager.maxLifeTime),
|
|
|
Expires: time.Now().Add(time.Duration(manager.maxLifeTime)),
|
|
|
//MaxAge和Expires都可以设置cookie持久化时的过期时长,Expires是老式的过期方法,
|
|
|
// 如果可以,应该使用MaxAge设置过期时间,但有些老版本的浏览器不支持MaxAge。
|
|
|
// 如果要支持所有浏览器,要么使用Expires,要么同时使用MaxAge和Expires。
|
|
|
}
|
|
|
http.SetCookie(w, &cookie)
|
|
|
} else {
|
|
|
sid, _ := url.QueryUnescape(cookie.Value) //反转义特殊符号
|
|
|
session, _ = manager.provider.SessionRead(sid)
|
|
|
}
|
|
|
return session
|
|
|
}
|
|
|
|
|
|
//销毁session 同时删除cookie
|
|
|
func (manager *Manager) SessionDestroy(w http.ResponseWriter, r *http.Request) {
|
|
|
cookie, err := r.Cookie(manager.cookieName)
|
|
|
if err != nil || cookie.Value == "" {
|
|
|
return
|
|
|
} else {
|
|
|
manager.lock.Lock()
|
|
|
defer manager.lock.Unlock()
|
|
|
sid, _ := url.QueryUnescape(cookie.Value)
|
|
|
manager.provider.SessionDestroy(sid)
|
|
|
expiration := time.Now()
|
|
|
cookie := http.Cookie{
|
|
|
Name: manager.cookieName,
|
|
|
Path: "/",
|
|
|
HttpOnly: true,
|
|
|
Expires: expiration,
|
|
|
MaxAge: -1}
|
|
|
http.SetCookie(w, &cookie)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
func (manager *Manager) GC() {
|
|
|
manager.lock.Lock()
|
|
|
defer manager.lock.Unlock()
|
|
|
manager.provider.SessionGC(manager.maxLifeTime)
|
|
|
time.AfterFunc(time.Duration(manager.maxLifeTime), func() { manager.GC() })
|
|
|
} |