Go 尝试实现session管理
package session
import (
"fmt"
"github.com/satori/go.uuid"
"log"
"net/http"
"net/url"
"sync"
"time"
)
const (
CookieName = "goodsCool"
Timeout = 60 * 30
)
var Cmgr *ConversationManager
func init() {
Cmgr = &ConversationManager{
CookieName: CookieName,
MaxAge: Timeout,
Storage: MemStorage{DataStorage: make(map[string]interface{}, 0)},
}
go Cmgr.GC()
}
type MemStorage struct {
DataStorage map[string]interface{}
Lock sync.Mutex
ReadWriteLock sync.RWMutex
}
func (ms *MemStorage) Store(key string, value interface{}) bool {
ms.Lock.Lock()
storage := ms.DataStorage
storage[key] = value
ms.Lock.Unlock()
return true
}
func (ms *MemStorage) Load(key string) (value interface{}, ok bool) {
ms.ReadWriteLock.RLock()
storage := ms.DataStorage
value = storage[key]
if value != nil {
ok = true
} else {
ok = false
}
ms.ReadWriteLock.RUnlock()
return
}
func (ms *MemStorage) Delete(key string) (value interface{}) {
ms.Lock.Lock()
storage := ms.DataStorage
if v, ok := storage[key]; ok {
value = v
}
delete(storage, key)
ms.Lock.Unlock()
return
}
func (ms *MemStorage) Range() {
for key, value := range ms.DataStorage {
fmt.Printf(" 遍历 %v,%v\n", key, value)
}
}
type IConversationManager interface {
Create(cid string) (conversation *Conversation, err error)
Destroy(cid string) (b bool, err error)
GC()
}
type IConversation interface {
Get(key interface{}) (info interface{}, err error)
Set(key interface{}, value interface{}) (b bool, err error)
}
type Conversation struct {
LastAccessTime time.Time
Cid string
CSData MemStorage
MaxAge int64
}
type ConversationManager struct {
CookieName string
MaxAge int64
Storage MemStorage
}
//实现 Create / Distroy / Get / Set 方法
//创建会话
func (cm *ConversationManager) Create(cid string) (*Conversation, error) {
//根据cid 创建一个conversatioin
storage := cm.Storage
value, ok := storage.Load(cid)
if ok {
conversation := value.(*Conversation)
return conversation, nil
}
m := MemStorage{DataStorage: make(map[string]interface{}, 0)}
c2 := &Conversation{
LastAccessTime: time.Now(),
Cid: cid,
CSData: m,
MaxAge: 60 * 30,
}
cm.Storage.Store(cid, c2)
return c2, nil
}
//getConversation
func (cm *ConversationManager) GetConversation(cid string) (conversation *Conversation) {
storage := cm.Storage
value, ok := storage.Load(cid)
if ok {
conversation = value.(*Conversation)
return conversation
}
return conversation
}
// 销毁cid 对应的会话
func (cm *ConversationManager) Destroy(cid string) (b bool, err error) {
if _, ok := cm.Storage.Load(cid); ok {
cm.Storage.Delete(cid)
}
return true, nil
}
func (cm *ConversationManager) GetConversationData(r *http.Request) (mem *MemStorage) {
cookie, _ := r.Cookie(CookieName)
if cookie != nil {
conversationId := cookie.Value
conversation := Cmgr.GetConversation(conversationId)
return &(conversation.CSData)
} else {
return nil
}
}
// 会话域存储Get
func (c *Conversation) Get(key string) (interface{}, error) {
//log.Println("从", c.Cid, "中取数据:key=", key)
if load, ok := c.CSData.Load(key); ok {
return load, nil
} else {
return nil, nil
}
}
// 会话域存储Set
func (c *Conversation) Set(key string, value interface{}) (b bool, err error) {
c.CSData.Store(key, value)
return true, nil
}
// ConversationManager GC
func (cm *ConversationManager) GC() {
for {
time.Sleep(time.Second * 10)
log.Println("GC ing ...")
storage := cm.Storage.DataStorage
duration := time.Duration(cm.MaxAge * 1000 * 1000 * 1000)
now := time.Now()
for cid, conversation := range storage {
//log.Printf("GC 遍历:%v,%v\n", cid, conversation)
if c, ok := conversation.(*Conversation); ok {
expire := c.LastAccessTime.Add(duration)
//log.Println("如果 expire 大于当前时间:", expire, " 当前时间:now=", now)
if now.After(expire) {
//将conversation销毁
log.Println("销毁Conversation: Cid=", cid)
cm.Destroy(cid)
}
}
}
}
}
// 给普通链接处理会话,没有就创建同时处理客户端
//的cookie
func (cm *ConversationManager) ManagerNormalRequest(w http.ResponseWriter, r *http.Request) (conversation *Conversation, e error) {
cookie, err := r.Cookie(CookieName)
if err != nil {
e = err
}
var conversationId string
if cookie == nil {
conversationId, err = url.QueryUnescape(uuid.Must(uuid.NewV4(), nil).String())
if err != nil {
e = err
return
}
} else {
conversationId = cookie.Value
}
//创建Conversation,如果有就返回
cs, err := cm.Create(conversationId)
if err != nil {
e = err
return
}
cs.LastAccessTime = time.Now()
expire := cs.LastAccessTime.Add(time.Duration(cm.MaxAge * 1000 * 1000 * 1000))
//log.Println("expire: ", expire)
//设置cookie
c := http.Cookie{
Name: CookieName,
Value: conversationId,
Path: "/",
//Domain: "",
Expires: expire,
RawExpires: "",
MaxAge: int(cm.MaxAge),
Secure: false,
HttpOnly: false,
SameSite: 0,
Raw: "",
Unparsed: nil,
}
//log.Println(c)
http.SetCookie(w, &c)
return cs, e
}
func UUID() (string, error) {
conversationId, err := url.QueryUnescape(uuid.Must(uuid.NewV4(), nil).String())
return conversationId, err
}
func (cm *ConversationManager) ManagerLogin(loginUser interface{}, w http.ResponseWriter, r *http.Request) {
cookie, _ := r.Cookie(CookieName)
//获取当前时间
now := time.Now()
var conversationId string
if cookie != nil {
//log.Println("已经有了cookie ==>", cookie)
conversationId = cookie.Value
} else {
conversationId, _ = UUID()
//log.Println("没有cookie,创建会话 cid=", conversationId)
}
expire := now.Add(time.Duration(Cmgr.MaxAge * 1000 * 1000 * 1000))
//设置cookie
cookie2 := http.Cookie{
Name: CookieName,
Value: conversationId,
Path: "/",
// Domain: "",
Expires: expire,
RawExpires: "",
MaxAge: 0,
Secure: false,
HttpOnly: false,
SameSite: 0,
Raw: "",
Unparsed: nil,
}
//存放登录数据
var conversation *Conversation
conversation = Cmgr.GetConversation(conversationId)
if conversation == nil {
create, _ := Cmgr.Create(cookie2.Value)
conversation = create
}
conversation.LastAccessTime = now
conversation.Set("user", loginUser)
log.Println("存放了User: conversationId:", conversation.Cid, "存放的用户:", loginUser)
http.SetCookie(w, &cookie2)
}
使用非常简单,添加Gin 中间件
func ConversationManager() gin.HandlerFunc {
return func(c *gin.Context) {
url := c.Request.URL
// log.Println("当前访问链接:", url.String())
if url.Path == "/login" {
//如果是登录链接,就放行
c.Next()
session.Cmgr.ManagerNormalRequest(c.Writer, c.Request)
return
}
//判断有没有cookie ,若有cookie 就根据ConversationId寻找后台对应的Conversation
//判断Conversation中有没有登录用户
cookie, _ := c.Request.Cookie(session.CookieName)
if cookie != nil {
conversation := session.Cmgr.GetConversation(cookie.Value)
if conversation != nil {
login, _ := conversation.Get("user")
if login == nil {
session.Cmgr.ManagerNormalRequest(c.Writer, c.Request)
c.HTML(200, "login.tmpl", nil)
} else {
//user := login.(*models.User)
//log.Println("conversationId:", conversation.Cid, " 用户: ", user.ToString())
c.Next()
}
} else {
session.Cmgr.ManagerNormalRequest(c.Writer, c.Request)
c.HTML(200, "login.tmpl", nil)
}
} else {
session.Cmgr.ManagerNormalRequest(c.Writer, c.Request)
c.HTML(200, "login.tmpl", nil)
}
}
}
func main() {
router := gin.Default()
//router.Use(middleware.Cors())
router.StaticFS("/static", http.Dir("src/static"))
router.StaticFS("/image/user", http.Dir(imagePath))
router.LoadHTMLGlob("src/views/admin/*")
router.NoRoute(controllers.Handle404)
router.Use(middleware.ConversationManager())
router.GET("/", controllers.Index)
}
func LoginAction(c *gin.Context) {
userName := c.PostForm("username")
passWord := c.PostForm("password")
u := &models.User{UserName: userName, PassWord: passWord}
login, err := u.Login(u)
if err != nil {
log.Println(err)
}
if login == nil {
c.HTML(http.StatusOK, "login.tmpl", nil)
} else {
session.Cmgr.ManagerLogin(login, c.Writer, c.Request)
data := make(map[string]interface{})
data["loginUser"] = login
c.HTML(http.StatusOK, "index.tmpl", data)
}
}

浙公网安备 33010602011771号