Go 如何使用session

53 篇文章 0 订阅
27 篇文章 0 订阅

Go 语言实现操作session不像cookie那样,net/http包里有现成函数可以很方便的使用,一些web服务用到session的话,没办法地自己敲代码实现。

Go具体实现session:

  • 服务端可以通过内存、redis、数据库等存储session数据(本例只有内存)。
  • 通过cookie将唯一SessionID发送到客户端

session.go

package session

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

memory.go

package memory

import (
	"container/list"
	"example/example/public/session"
	"sync"
	"time"
)

var pder = &FromMemory{list: list.New()}

func init() {
	pder.sessions = make(map[string]*list.Element, 0)
	//注册  memory 调用的时候一定有一致
	session.Register("memory", pder)
}

//session实现
type SessionStore struct {
	sid              string                      //session id 唯一标示
	LastAccessedTime time.Time                   //最后访问时间
	value            map[interface{}]interface{} //session 里面存储的值
}

//设置
func (st *SessionStore) Set(key, value interface{}) error {
	st.value[key] = value
	pder.SessionUpdate(st.sid)
	return nil
}

//获取session
func (st *SessionStore) Get(key interface{}) interface{} {
	pder.SessionUpdate(st.sid)
	if v, ok := st.value[key]; ok {
		return v
	} else {
		return nil
	}
	return nil
}

//删除
func (st *SessionStore) Delete(key interface{}) error {
	delete(st.value, key)
	pder.SessionUpdate(st.sid)
	return nil
}
func (st *SessionStore) SessionID() string {
	return st.sid
}

//session来自内存 实现
type FromMemory struct {
	lock     sync.Mutex               //用来锁
	sessions map[string]*list.Element //用来存储在内存
	list     *list.List               //用来做 gc
}

func (frommemory *FromMemory) SessionInit(sid string) (session.Session, error) {
	frommemory.lock.Lock()
	defer frommemory.lock.Unlock()
	v := make(map[interface{}]interface{}, 0)
	newsess := &SessionStore{sid: sid, LastAccessedTime: time.Now(), value: v}
	element := frommemory.list.PushBack(newsess)
	frommemory.sessions[sid] = element
	return newsess, nil
}

func (frommemory *FromMemory) SessionRead(sid string) (session.Session, error) {
	if element, ok := frommemory.sessions[sid]; ok {
		return element.Value.(*SessionStore), nil
	} else {
		sess, err := frommemory.SessionInit(sid)
		return sess, err
	}
	return nil, nil
}

func (frommemory *FromMemory) SessionDestroy(sid string) error {
	if element, ok := frommemory.sessions[sid]; ok {
		delete(frommemory.sessions, sid)
		frommemory.list.Remove(element)
		return nil
	}
	return nil
}

func (frommemory *FromMemory) SessionGC(maxLifeTime int64) {
	frommemory.lock.Lock()
	defer frommemory.lock.Unlock()
	for {
		element := frommemory.list.Back()
		if element == nil {
			break
		}
		if (element.Value.(*SessionStore).LastAccessedTime.Unix() + maxLifeTime) <
			time.Now().Unix() {
			frommemory.list.Remove(element)
			delete(frommemory.sessions, element.Value.(*SessionStore).sid)
		} else {
			break
		}
	}
}
func (frommemory *FromMemory) SessionUpdate(sid string) error {
	frommemory.lock.Lock()
	defer frommemory.lock.Unlock()
	if element, ok := frommemory.sessions[sid]; ok {
		element.Value.(*SessionStore).LastAccessedTime = time.Now()
		frommemory.list.MoveToFront(element)
		return nil
	}
	return nil
}

memory.go里面是sesson.go 的Provider 和Session 接口的具体实现,通过这种方式可以灵活的扩展存取session数据的方式。

调用实例:

main.go

package main

import (
	_ "example/example/public/memory"  //这里修改成你存放menory.go相应的目录
	"example/example/public/session" //这里修改成你存放session.go相应的目录
	"fmt"
	"github.com/aliyun/alibaba-cloud-sdk-go/sdk/log"
	"net/http"
)

var globalSessions *session.Manager

func init() {
	var err error
	globalSessions, err = session.NewSessionManager("memory", "goSessionid", 3600)
	if err != nil {
		fmt.Println(err)
		return
	}
	go globalSessions.GC()
	fmt.Println("fd")
}

func sayHelloHandler(w http.ResponseWriter, r *http.Request) {

	cookie, err := r.Cookie("name")
	if err == nil {
		fmt.Println(cookie.Value)
		fmt.Println(cookie.Domain)
		fmt.Println(cookie.Expires)
	}
	//fmt.Fprintf(w, "Hello world!\n") //这个写入到w的是输出到客户端的
}
func login(w http.ResponseWriter, r *http.Request) {
	sess := globalSessions.SessionStart(w, r)
	val := sess.Get("username")
	if val != nil {
		fmt.Println(val)
	} else {
		sess.Set("username", "jerry")
		fmt.Println("set session")
	}
}
func loginOut(w http.ResponseWriter, r *http.Request) {
	//销毁
	globalSessions.SessionDestroy(w, r)
	fmt.Println("session destroy")
}

func main() {
	http.HandleFunc("/", sayHelloHandler) //	设置访问路由
	http.HandleFunc("/login", login)
	http.HandleFunc("/loginout", loginOut) //销毁
	log.Fatal(http.ListenAndServe(":8080", nil))
}

memory 包的应用方式用下划线,只需执行 memory的init方法即可。

运行 main.go

访问 http://localhost:8080/login 设置session

服务端输出:

set session

jerry

访问http://localhost:8080/loginout 销毁session

服务端输出:

session destroy

项目用例:https://github.com/guyan0319/golang_development_notes

  • 8
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
在 Go 语言中,可以使用第三方库实现 session 功能。常用的 session 库有: - [gorilla/sessions](https://github.com/gorilla/sessions) - [gin-contrib/sessions](https://github.com/gin-contrib/sessions) - [go-session/session](https://github.com/go-session/session) 其中,`gorilla/sessions` 库是比较常用的库之一,它提供了简单易用的 session 功能。 使用 `gorilla/sessions` 库,需要先安装该库: ``` go get github.com/gorilla/sessions ``` 然后,在代码中引入该库: ```go import ( "github.com/gorilla/sessions" "net/http" ) ``` 接着,可以创建一个 session 存储对象: ```go store := sessions.NewCookieStore([]byte("secret-key")) ``` 在上述代码中,`NewCookieStore()` 函数创建了一个基于 cookiesession 存储对象,第一个参数是用于加密 session 的密钥,第二个参数是用于加密 cookie 的密钥。 然后,可以使用 `store.Get()` 方法获取一个 session 对象: ```go session, err := store.Get(request, "session-name") ``` 在上述代码中,`store.Get()` 方法根据请求对象和 session 名称获取一个 session 对象。如果该 session 不存在,会自动创建一个新的 session 对象。 接着,可以使用 session 对象的 `Set()` 和 `Get()` 方法设置和获取 session 数据: ```go session.Values["key"] = "value" value := session.Values["key"] ``` 在上述代码中,我们使用 `session.Values` 属性来设置和获取 session 数据。 最后,需要调用 session 对象的 `Save()` 方法将 session 数据保存到存储介质中: ```go session.Save(request, response) ``` 在上述代码中,`session.Save()` 方法将 session 数据保存到响应对象中,客户端会在下次请求时发送该 session 数据给服务端。 完整示例代码如下: ```go package main import ( "github.com/gorilla/sessions" "net/http" ) func main() { store := sessions.NewCookieStore([]byte("secret-key")) http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { session, err := store.Get(r, "session-name") if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } session.Values["key"] = "value" value := session.Values["key"] if value == nil { http.Error(w, "No value found in session", http.StatusInternalServerError) return } session.Save(r, w) w.Write([]byte("Session value: " + value.(string))) }) http.ListenAndServe(":8080", nil) } ``` 在上述示例代码中,我们创建了一个基于 cookiesession 存储对象,然后在请求处理函数中获取 session 对象,设置和获取 session 数据,最后将 session 数据保存到响应对象中。
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值