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)
	}
}

  

posted @ 2020-01-13 17:08  和平鸽  阅读(700)  评论(0编辑  收藏  举报