Go 如何使用session
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相关推荐
- 2022面试200题目和答案分布式+微服务+MYSQL+Redis+JVM+Spring
200题目和答案分布式+微服务+MYSQL+Redis+JVM+Spring等等 带图MD在资源https://download.csdn.net/download/m0_47987937/86509 ...
- Struts2 Cannot create a session after the response has been committed 一个不起眼的错误
严重: Servlet.service() for servlet default threw exception java.lang.IllegalStateException: Cannot c ...
- 解决nginx负载均衡的session共享问题
之前有写过ubuntu环境下搭建nginx环境,今天来谈一下nginx session共享问题,查了一些资料,看了一些别人写的文档,总结如下,实现nginx session的共享服务器有多台,用ngi ...
- 你想了解的Cookie和Session就在这~
目录 1.会话的概念 2.保存会话的两种技术 3.Cookie 4.Session(重点) 1.会话的概念 我们知道session的意思就是会话,Cookie和Session 是两种会话技术,我们首先 ...
- Cookie和Session的区别与联系
Cookie和Session Session 会话的理解 Session的作用 HTTP协议的无状态特点 Session的实现原理(重点) Session常用方法: Cookie 基本介绍 经典案例 ...
- php – Laravel 7 Session Lifetime
根据Laravel config / session.php /* |----------------------------------------------------------------- ...
- [JAVA EE]session 和 token 机制
HTTP 协议是无状态的. 即服务器对于客户端每次发送的请求都认为它是一个新的请求. 本次请求和上次请求无法判断是不是同一个客户端操作的. 随着 Web 应用的发展,如在线购物网站,需要登录的网站等, ...
- redis缓存和cookie实现Session共享
分布式项目中要实现单点登录(SSO - Single Sign On):对于同一个客户端(例如 Chrome 浏览器),只要登录了一个子站(例如 a.com),则所有子站(b.com.c.com)都认 ...
- 负载均衡中使用 Redis 实现共享 Session
最近在研究Web架构方面的知识,包括数据库读写分离,Redis缓存和队列,集群,以及负载均衡(LVS),今天就来先学习下我在负载均衡中遇到的问题,那就是session共享的问题. 一.负载均衡 负载均 ...
- session文件无法并发操作
session_start():打开服务器上的session文件. session_commit():会把$_SESSION数组的内容写入到服务器上的session文件中,但不会清空$_SESSION ...
最新文章
- matlab调用python函数未定义函数类_从零学习Python—调用函数def用法(下)
- Java基础—复制之深拷贝与浅拷贝
- Linux下memcache的安装和启动(很好)
- 专访福建移动林志云: 5G使能,运营商全面进入数字化转型之路
- 十大经典排序算法1(Python版本)
- python-45-ajax数据序列化
- 按值传递时 php必须复制值,PHP开发笔试题及答案(一)
- python 怎么注释_python的代码怎么写注释
- C语言基础知识(期末喽)
- tqdm的版本问题导致tensorflow_datasets无法加载
- web前端入门到实战:CSS3中width值为max/min-content及fit-content的理解
- 三阶魔方中心块调整公式及助记方法
- Windows 内存机制说明
- oracle数据库查看归档日志文件,查看oracle归档日志路径
- 快速处理-小程序/uniapp,showToast没有效果
- 安卓游戏开发一(超级玛丽)
- 微服务系统架构设计系列 - RateLimiter - 1. 限流器简介与一般算法
- 40岁男人娶20岁女孩
- 5G有源、无源室分对比
- 通过itextpdf操作PDF,动态向PDF文件最后一页添加图片