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

Go具体实现session:

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

session.go

package sessionimport ("crypto/rand""encoding/base64""fmt""io""net/http""net/url""sync""time"
)//session存储方式接口
type Provider interface {//初始化一个session,sid根据需要生成后传入SessionInit(sid string) (Session, error)//根据sid,获取sessionSessionRead(sid string) (Session, error)//销毁sessionSessionDestroy(sid string) error//回收SessionGC(maxLifeTime int64)
}//Session操作接口
type Session interface {Set(key, value interface{}) errorGet(key interface{}) interface{}Delete(ket interface{}) errorSessionID() string
}type Manager struct {cookieName  stringlock        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 memoryimport ("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] = valuepder.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] = elementreturn 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 mainimport (_ "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.Managerfunc init() {var err errorglobalSessions, 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

Go 如何使用session相关推荐

  1. 2022面试200题目和答案分布式+微服务+MYSQL+Redis+JVM+Spring

    200题目和答案分布式+微服务+MYSQL+Redis+JVM+Spring等等 带图MD在资源https://download.csdn.net/download/m0_47987937/86509 ...

  2. Struts2 Cannot create a session after the response has been committed 一个不起眼的错误

    严重: Servlet.service() for servlet default threw exception java.lang.IllegalStateException:  Cannot c ...

  3. 解决nginx负载均衡的session共享问题

    之前有写过ubuntu环境下搭建nginx环境,今天来谈一下nginx session共享问题,查了一些资料,看了一些别人写的文档,总结如下,实现nginx session的共享服务器有多台,用ngi ...

  4. 你想了解的Cookie和Session就在这~

    目录 1.会话的概念 2.保存会话的两种技术 3.Cookie 4.Session(重点) 1.会话的概念 我们知道session的意思就是会话,Cookie和Session 是两种会话技术,我们首先 ...

  5. Cookie和Session的区别与联系

    Cookie和Session Session 会话的理解 Session的作用 HTTP协议的无状态特点 Session的实现原理(重点) Session常用方法: Cookie 基本介绍 经典案例 ...

  6. php – Laravel 7 Session Lifetime

    根据Laravel config / session.php /* |----------------------------------------------------------------- ...

  7. [JAVA EE]session 和 token 机制

    HTTP 协议是无状态的. 即服务器对于客户端每次发送的请求都认为它是一个新的请求. 本次请求和上次请求无法判断是不是同一个客户端操作的. 随着 Web 应用的发展,如在线购物网站,需要登录的网站等, ...

  8. redis缓存和cookie实现Session共享

    分布式项目中要实现单点登录(SSO - Single Sign On):对于同一个客户端(例如 Chrome 浏览器),只要登录了一个子站(例如 a.com),则所有子站(b.com.c.com)都认 ...

  9. 负载均衡中使用 Redis 实现共享 Session

    最近在研究Web架构方面的知识,包括数据库读写分离,Redis缓存和队列,集群,以及负载均衡(LVS),今天就来先学习下我在负载均衡中遇到的问题,那就是session共享的问题. 一.负载均衡 负载均 ...

  10. session文件无法并发操作

    session_start():打开服务器上的session文件. session_commit():会把$_SESSION数组的内容写入到服务器上的session文件中,但不会清空$_SESSION ...

最新文章

  1. matlab调用python函数未定义函数类_从零学习Python—调用函数def用法(下)
  2. Java基础—复制之深拷贝与浅拷贝
  3. Linux下memcache的安装和启动(很好)
  4. 专访福建移动林志云: 5G使能,运营商全面进入数字化转型之路
  5. 十大经典排序算法1(Python版本)
  6. python-45-ajax数据序列化
  7. 按值传递时 php必须复制值,PHP开发笔试题及答案(一)
  8. python 怎么注释_python的代码怎么写注释
  9. C语言基础知识(期末喽)
  10. tqdm的版本问题导致tensorflow_datasets无法加载
  11. web前端入门到实战:CSS3中width值为max/min-content及fit-content的理解
  12. 三阶魔方中心块调整公式及助记方法
  13. Windows 内存机制说明
  14. oracle数据库查看归档日志文件,查看oracle归档日志路径
  15. 快速处理-小程序/uniapp,showToast没有效果
  16. 安卓游戏开发一(超级玛丽)
  17. 微服务系统架构设计系列 - RateLimiter - 1. 限流器简介与一般算法
  18. 40岁男人娶20岁女孩
  19. 5G有源、无源室分对比
  20. 通过itextpdf操作PDF,动态向PDF文件最后一页添加图片

热门文章

  1. 2022年天猫年货节红包时间,2022年1月7日20点开始
  2. 【Java】泛型(Generics)
  3. 全图文详细记录安装 免费SSL证书
  4. 基于用户画像的电影推荐系统论文
  5. python str转换为十六进制
  6. 升级mysql数据库版本时,对应配置文件的一些变化
  7. 关于正态总体的样本均值与样本方差的重要结论
  8. c语言中的原码反码补码,c语言中的原码 反码 补码
  9. emgucv 在工具中添加 ImageBox
  10. Crypto++编译使用