session
package session
import (
"fmt"
"sync"
"math/rand"
"encoding/base64"
"net/http"
"net/url"
"time"
)
//定义一个全局的 session 管理器
type Manager struct {
cookieName string // private cookiename
lock sync.Mutex // protects session
provider Provider
maxLifeTime int64 //最大无操作生命周期
}
//全局唯一的 Session ID
func (manager *Manager) sessionId() string {
b := make([]byte, 32)
//生成32位随机字节
if _, err := rand.Read(b); err != nil {
return ""
}
return base64.URLEncoding.EncodeToString(b)
}
//session 创建,为每个来访用户分配或获取与他相关连的 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)}
http.SetCookie(w, &cookie)
} else {
sid, _ := url.QueryUnescape(cookie.Value)//cookie.Value就是每次携带的session id
session, _ = manager.provider.SessionRead(sid)
}
return
}
//session 重置,当用户退出应用的时候,我们需要对该用户的 session 数据进行销毁操作
// Destroy sessionid
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()
manager.provider.SessionDestroy(cookie.Value)
expiration := time.Now()//告知浏览器cookie失效
cookie := http.Cookie{Name: manager.cookieName, Path: "/", HttpOnly: true, Expires: expiration, MaxAge: -1}
http.SetCookie(w, &cookie)
}
}
//session 销毁,确保过期session失效
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() })
}
func NewManager(provideName, cookieName string, maxLifeTime int64) (*Manager, error) {
provider, ok := provides[provideName]
if !ok {
return nil, fmt.Errorf("session: unknown provide %q (forgotten import?)", provideName)
}
return &Manager{provider: provider, cookieName: cookieName, maxLifeTime: maxLifeTime}, nil
}
//表征 session 管理器底层存储结构,存储在内存、数据库或者文件
type Provider interface {
SessionInit(sid string) (Session, error)//函数实现 Session 的初始化,操作成功则返回此新的 Session 变量
SessionRead(sid string) (Session, error)//函数返回 sid 所代表的 Session 变量,如果不存在,那么将以 sid 为参数调用 SessionInit 函数创建并返回一个新的 Session 变量
SessionDestroy(sid string) error//函数用来销毁 sid 对应的 Session 变量
SessionGC(maxLifeTime int64)//根据 maxLifeTime 来删除过期的数据
}
//定义session操作
type Session interface {
Set(key, value interface{}) error // set session value
Get(key interface{}) interface{} // get session value
Delete(key interface{}) error // delete session value
SessionID() string // back current sessionID
}
var provides = make(map[string]Provider)
// Register makes a session provide available by the provided name.
// If Register is called twice with the same name or if driver is nil,
// it panics.
func Register(name string, provider Provider) {
if provider == nil {
panic("session: Register provider is nil")
}
if _, dup := provides[name]; dup {
panic("session: Register called twice for provider " + name)
}
provides[name] = provider
}
package memory
import (
"container/list"
"6.2/session"
"sync"
"time"
)
//Session会话对象
type SessionStore struct {
sid string // session id唯一标示
timeAccessed 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
}
func (st *SessionStore) Get(key interface{}) interface{} {
pder.SessionUpdate(st.sid)
if v, ok := st.value[key]; ok {
return v
} else {
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
}
type Provider struct {
lock sync.Mutex // 用来锁
sessions map[string]*list.Element // 用来存储在内存(双向链表存储的节点)
list *list.List // 双向链表用来做 gc
}
//实现 Session 的初始化,操作成功则返回此新的 Session 变量
func (pder *Provider) SessionInit(sid string) (session.Session, error) {
pder.lock.Lock()
defer pder.lock.Unlock()
v := make(map[interface{}]interface{}, 0)
newsess := &SessionStore{sid: sid, timeAccessed: time.Now(), value: v}
element := pder.list.PushFront(newsess)
pder.sessions[sid] = element
return newsess, nil
}
//返回 sid 所代表的 Session 变量,如果不存在,那么将以 sid 为参数调用 SessionInit 函数创建并返回一个新的 Session 变量,(cookie.Value从哪来)
func (pder *Provider) SessionRead(sid string) (session.Session, error) {
if element, ok := pder.sessions[sid]; ok {
return element.Value.(*SessionStore), nil
} else {
sess, err := pder.SessionInit(sid)
return sess, err
}
return nil, nil
}
//用来销毁 sid 对应的 Session 变量
func (pder *Provider) SessionDestroy(sid string) error {
if element, ok := pder.sessions[sid]; ok {
delete(pder.sessions, sid)
pder.list.Remove(element)
return nil
}
return nil
}
//根据 maxLifeTime 来删除过期的数据
func (pder *Provider) SessionGC(maxlifetime int64) {
pder.lock.Lock()
defer pder.lock.Unlock()
for {
element := pder.list.Back()//链表最后一个元素
if element == nil {
break
}
if (element.Value.(*SessionStore).timeAccessed.Unix() + maxlifetime) < time.Now().Unix() {
pder.list.Remove(element)
delete(pder.sessions, element.Value.(*SessionStore).sid)
} else {
break
}
}
}
func (pder *Provider) SessionUpdate(sid string) error {
pder.lock.Lock()
defer pder.lock.Unlock()
if element, ok := pder.sessions[sid]; ok {
element.Value.(*SessionStore).timeAccessed = time.Now()
pder.list.MoveToFront(element)
return nil
}
return nil
}
var pder = &Provider{list: list.New()}
func init() {
pder.sessions = make(map[string]*list.Element, 0)
session.Register("memory", pder)
}
package main
import (
_ "6.2/memory"
"6.2/session"
"html/template"
"log"
"net/http"
"time"
)
var globalSessions *session.Manager
// 然后在 init 函数中初始化
func init() {
globalSessions, _ = session.NewManager("memory", "gosessionid", 3600)
go globalSessions.GC()
}
func login(w http.ResponseWriter, r *http.Request) {
sess := globalSessions.SessionStart(w, r)
r.ParseForm()
if r.Method == "GET" {
t, _ := template.ParseFiles("login.gtpl")
w.Header().Set("Content-Type", "text/html")
t.Execute(w, sess.SessionID())
} else {
sess.Set("username", r.Form["username"])
sess.Set("password", r.Form["password"])
template.HTMLEscape(w, []byte(r.Form.Get("username"))) // 输出到客户端
http.Redirect(w, r, "/", 302)
}
}
func count(w http.ResponseWriter, r *http.Request) {
sess := globalSessions.SessionStart(w, r)
createtime := sess.Get("createtime")
if createtime == nil {
sess.Set("createtime", time.Now().Unix())
} else if (createtime.(int64) + 360) < (time.Now().Unix()) {
globalSessions.SessionDestroy(w, r)
sess = globalSessions.SessionStart(w, r)
}
ct := sess.Get("countnum")
if ct == nil {
sess.Set("countnum", 1)
} else {
sess.Set("countnum", (ct.(int) + 1))
}
t, _ := template.ParseFiles("count.gtpl")
w.Header().Set("Content-Type", "text/html")
t.Execute(w, sess.Get("countnum"))
}
func main() {
http.HandleFunc("/", login) // 设置访问的路由
http.HandleFunc("/count", count)
err := http.ListenAndServe(":9090", nil) // 设置监听的端口
if err != nil {
log.Fatal("ListenAndServe: ", err)
}
}
//总结
//SessionStore是一个会话对象,被memory.Provider进行管理,在memory初始化时就会将memory.Provider对象进行注册
//Manager作为一个session管理器,new出来时便会根据session 管理器底层存储结构来选择之前注册好的Provider,作为其数据成员
//Manager来通过其数据成员Provider来管理session
//疑问
//sessionID 的值只允许 cookie 设置,而不是通过 URL 重置方式设置?如何实现
这章看着太难受了,非常感谢 :joy: :joy: