gin框架实践连载番外篇造一个session的轮子学废

引言

  • go没有官方类库来实现session
  • 之所以选择自己造个轮子是为了更加容易看大佬们轮子的思路,也算是一种积累
  • 一起学习共同进步
  • 代码地址
  • 案例代码地址

1、架构图

直接上图

2、Manager定义

2.1 获取session对象

sessionid由客户端cookie存储(其实也可以支持在header自定义或者url传参)

func (manager *Manager) GetSession(w http.ResponseWriter, r *http.Request) (s *Session) {
	/*加锁*/
	manager.lock.Lock()
	defer manager.lock.Unlock()

	cookie, err := r.Cookie(manager.CookieName)
	if err != nil || cookie.Value == "" {
		/*获取sid*/
		sid := manager.sessionId()
		s = manager.NewSesssion(sid)

		cookie := http.Cookie{
			Name:     manager.CookieName,
			Value:    url.QueryEscape(sid),
			Path:     "/",
			HttpOnly: true,
			MaxAge:   manager.maxlifetime,
		}

		http.SetCookie(w, &cookie)
	} else {
		sid, _ := url.QueryUnescape(cookie.Value)
		s = manager.NewSesssion(sid)
	}

	return s
}
复制代码

2.2 注册store

func Register(name string, store StoreInterface) {
	if store == nil {
		log.Panic("Session: Register store is nil")
	}

	name = strings.ToLower(name)

	if _, ok := Stores[name]; ok {
		log.Panic("Session: Register store is exist")
	}

	Stores[name] = store
}

复制代码

3、session定义

type Session struct {
	ID      string
	store   StoreInterface
	mamager *Manager
	ttl     int
}

func (s *Session) DestroySession(w http.ResponseWriter, r *http.Request) {
	cookie := http.Cookie{
		Name:   s.mamager.CookieName,
		Value:  "",
		Path:   "/",
		MaxAge: -1,
	}

	http.SetCookie(w, &cookie)
}

func (s *Session) Set(key string, value interface{}) error {
	return s.store.Set(s.ID, key, value, s.ttl)
}

func (s *Session) Get(key string) (interface{}, error) {
	return s.store.Get(s.ID, key)
}

func (s *Session) Delete(key string) error {
	return s.store.Remove(s.ID, key)
}
复制代码

4、session_store定义

type StoreInterface interface {
	//删除某个key的值
	Remove(Sid string, key string) error
	//获取某个key的值
	Get(Sid string, key string) (interface{}, error)
	//设置某个key的值
	Set(Sid string, key string, value interface{}, ttl int) error
}
复制代码

5、使用redis实现store

var Ctx context.Context

type RedisStore struct {
	reidsClient *redis.Client
}

func NewRedis(addr, password string, db, PoolSize int) *RedisStore {

	ClientRedis := redis.NewClient(&redis.Options{
		Addr:     addr,
		Password: password, // no password set
		DB:       db,       // use default DB
		PoolSize: PoolSize, // 连接池大小
		//MinIdleConns: 5,
	})

	Ctx = ClientRedis.Context()

	err := ClientRedis.Ping(Ctx).Err()
	if err != nil {
		log.Panic("Cache store redis:", err)
	}

	return &RedisStore{
		reidsClient: ClientRedis,
	}
}

func RegisterRedis(addr, password string, db, PoolSize int) {
	gosession.Register("redis", NewRedis(addr, password, db, PoolSize))
}

func (s *RedisStore) FmtKey(Sid, key string) string {
	return fmt.Sprintf("%s_%s", Sid, key)
}

func (s *RedisStore) Get(Sid, key string) (interface{}, error) {
	return s.reidsClient.Get(Ctx, s.FmtKey(Sid, key)).Result()
}

func (s *RedisStore) Set(Sid, key string, value interface{}, d int) error {
	return s.reidsClient.Set(Ctx, s.FmtKey(Sid, key), value, time.Duration(d)*time.Second).Err()
}

func (s *RedisStore) Remove(Sid, key string) error {
	return s.reidsClient.Del(Ctx, s.FmtKey(Sid, key)).Err()
}
复制代码

6、demo

package main

import (
	"fmt"
	"log"
	"net/http"

	"github.com/18211167516/gosession"
	"github.com/18211167516/gosession/store"
	"github.com/gogf/gf/util/gconv"
)

var GloabSession *gosession.Manager

func init() {
	var err error
	store.RegisterRedis("192.168.99.100:6379", "", 10, 10)
	GloabSession, err = gosession.NewDefaultIDManager("redis", "gosession", 84600)
	if err != nil {
		log.Panic("Session Manager init ", err)
	}
}

func indexHandler(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "URL.Path = %q\n", r.URL.Path)
}

func helloHandler(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "URL.PATH=%q\n Header=%q\n", r.URL.Path, r.Header)
}

func login(w http.ResponseWriter, r *http.Request) {
	s := GloabSession.GetSession(w, r)
	v, _ := s.Get("isLogin")
	if gconv.Int(v) == 1 {
		fmt.Fprintf(w, "已登录")
	} else {
		s.Set("isLogin", 1)
		fmt.Fprintf(w, "正在登录")
	}
}

func logout(w http.ResponseWriter, r *http.Request) {
	s := GloabSession.GetSession(w, r)
	s.Delete("isLogin")
	s.DestroySession(w, r)
	fmt.Fprintf(w, "已退出登录")
}

func main() {
	http.HandleFunc("/", indexHandler)
	http.HandleFunc("/hello", helloHandler)
	http.HandleFunc("/login", login)
	http.HandleFunc("/logout", logout)
	http.ListenAndServe(":8080", nil)
}

复制代码

7、其它

与诸君共勉之,写的不好的地址给指正,希望对您有帮助,思路很重要

8、系列文章