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 重置方式设置?如何实现
讨论数量: 1

这章看着太难受了,非常感谢 :joy: :joy:

2年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!