You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

127 lines
3.5 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

package SessionUtil
import (
"crypto/rand"
"encoding/base64"
"fmt"
"io"
"net/http"
"net/url"
"sync"
"time"
)
//session存储方式接口
type Provider interface {
//初始化一个sessionsid根据需要生成后传入
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() })
}