這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
文章轉自https://blog.csdn.net/lzy_zhi_yuan/article/details/73127601
go裡沒有官方的的session,所以才去封裝了session管理器,具體步驟見代碼注釋,將在後面的文章進行講解具體實戰。
//Session操作介面,不同儲存方式的Sesion操作不同,實現也不同type Session interface { Set(key, value interface{}) Get(key interface{}) interface{} Remove(key interface{}) error GetId() string}//session實現type SessionFromMemory struct { sid string //唯一標示 lock sync.Mutex //一把互斥鎖 lastAccessedTime time.Time //最後訪問時間 maxAge int64 //逾時時間 data map[interface{}]interface{} //主要資料}//執行個體化func newSessionFromMemory() *SessionFromMemory { return &SessionFromMemory{ data: make(map[interface{}]interface{}), maxAge: 60 * 30, //預設30分鐘 }}//同一個會話均可調用,進行設定,改操作必須擁有排斥鎖func (si *SessionFromMemory) Set(key, value interface{}) { si.lock.Lock() defer si.lock.Unlock() si.data[key] = value}func (si *SessionFromMemory) Get(key interface{}) interface{} { if value := si.data[key]; value != nil { return value } return nil}func (si *SessionFromMemory) Remove(key interface{}) error { if value := si.data[key]; value != nil { delete(si.data, key) } return nil}func (si *SessionFromMemory) GetId() string { return si.sid}//session儲存方式介面,可以儲存在記憶體,資料庫或者檔案//分別實現該介面即可//如存入資料庫的CRUD操作type Storage interface { //初始化一個session,id根據需要產生後傳入 InitSession(sid string, maxAge int64) (Session, error) //根據sid,獲得當前session SetSession(session Session) error //銷毀session DestroySession(sid string) error //回收 GCSession()}//session來自記憶體type FromMemory struct { //由於session包含所有的請求 //並行時,保證資料獨立、一致、安全 lock sync.Mutex //互斥鎖 sessions map[string]Session}//執行個體化一個記憶體實現func newFromMemory() *FromMemory { return &FromMemory{ sessions: make(map[string]Session, 0), }}//初始換會話session,這個結構體操作實現Session介面func (fm *FromMemory) InitSession(sid string, maxAge int64) (Session, error) { fm.lock.Lock() defer fm.lock.Unlock() newSession := newSessionFromMemory() newSession.sid = sid if maxAge != 0 { newSession.maxAge = maxAge } newSession.lastAccessedTime = time.Now() fm.sessions[sid] = newSession //記憶體管理map return newSession, nil}//設定func (fm *FromMemory) SetSession(session Session) error { fm.sessions[session.GetId()] = session return nil}//銷毀sessionfunc (fm *FromMemory) DestroySession(sid string) error { if _, ok := fm.sessions[sid]; ok { delete(fm.sessions, sid) return nil } return nil}//監判逾時func (fm *FromMemory) GCSession() { sessions := fm.sessions //fmt.Println("gc session") if len(sessions) < 1 { return } //fmt.Println("current active sessions ", sessions) for k, v := range sessions { t := (v.(*SessionFromMemory).lastAccessedTime.Unix()) + (v.(*SessionFromMemory).maxAge) if t < time.Now().Unix() { //逾時了 fmt.Println("timeout-------->", v) delete(fm.sessions, k) } }}//--------------session_manager----------------------//管理Session,實際操作cookie,Storage//由於該結構體是整個應用層級的,寫、修改都需要枷鎖type SessionManager struct { //session資料最終需要在用戶端(瀏覽器)和伺服器各存一份 //用戶端時,存放在cookie中 cookieName string //存放方式,如記憶體,資料庫,檔案 storage Storage //逾時時間 maxAge int64 //由於session包含所有的請求 //並行時,保證資料獨立、一致、安全 lock sync.Mutex}//執行個體化一個session管理器func NewSessionManager() *SessionManager { sessionManager := &SessionManager{ cookieName: "lzy-cookie", storage: newFromMemory(), //預設以記憶體實現 maxAge: 60 * 30, //預設30分鐘 } go sessionManager.GC() return sessionManager}func (m *SessionManager) GetCookieN() string { return m.cookieName}//先判斷當前請求的cookie中是否存在有效session,存在返回,不存在建立func (m *SessionManager) BeginSession(w http.ResponseWriter, r *http.Request) Session { //防止處理時,進入另外的請求 m.lock.Lock() defer m.lock.Unlock() cookie, err := r.Cookie(m.cookieName) if err != nil || cookie.Value == "" { //如果當前請求沒有改cookie名字對應的cookie fmt.Println("-----------> current session not exists") //建立一個 sid := m.randomId() //根據儲存session方式,如記憶體,資料庫中建立 session, _ := m.storage.InitSession(sid, m.maxAge) //該方法有自己的鎖,多處調用到 maxAge := m.maxAge if maxAge == 0 { maxAge = session.(*SessionFromMemory).maxAge } //用session的ID於cookie關聯 //cookie名字和失效時間由session管理器維護 cookie := http.Cookie{ Name: m.cookieName, //這裡是並發不安全的,但是這個方法已上鎖 Value: url.QueryEscape(sid), //轉義特殊符號@#¥%+*-等 Path: "/", HttpOnly: true, MaxAge: int(maxAge), Expires: time.Now().Add(time.Duration(maxAge)), } http.SetCookie(w, &cookie) //設定到響應中 return session } else { //如果存在 sid, _ := url.QueryUnescape(cookie.Value) //反轉義特殊符號 session := m.storage.(*FromMemory).sessions[sid] //從儲存session介質中擷取 fmt.Println("session --------->", session) if session == nil { fmt.Println("-----------> current session is nil") //建立一個 //sid := m.randomId() //根據儲存session方式,如記憶體,資料庫中建立 newSession, _ := m.storage.InitSession(sid, m.maxAge) //該方法有自己的鎖,多處調用到 maxAge := m.maxAge if maxAge == 0 { maxAge = newSession.(*SessionFromMemory).maxAge } //用session的ID於cookie關聯 //cookie名字和失效時間由session管理器維護 newCookie := http.Cookie{ Name: m.cookieName, //這裡是並發不安全的,但是這個方法已上鎖 Value: url.QueryEscape(sid), //轉義特殊符號@#¥%+*-等 Path: "/", HttpOnly: true, MaxAge: int(maxAge), Expires: time.Now().Add(time.Duration(maxAge)), } http.SetCookie(w, &newCookie) //設定到響應中 return newSession } fmt.Println("-----------> current session exists") return session }}//更新逾時func (m *SessionManager) Update(w http.ResponseWriter, r *http.Request) { m.lock.Lock() defer m.lock.Unlock() cookie, err := r.Cookie(m.cookieName) if err != nil { return } t := time.Now() sid, _ := url.QueryUnescape(cookie.Value) sessions := m.storage.(*FromMemory).sessions session := sessions[sid].(*SessionFromMemory) session.lastAccessedTime = t sessions[sid] = session if m.maxAge != 0 { cookie.MaxAge = int(m.maxAge) } else { cookie.MaxAge = int(session.maxAge) } http.SetCookie(w, cookie)}//通過ID擷取sessionfunc (m *SessionManager) GetSessionById(sid string) Session { session := m.storage.(*FromMemory).sessions[sid] return session}//是否記憶體中存在func (m *SessionManager) MemoryIsExists(sid string) bool { _, ok := m.storage.(*FromMemory).sessions[sid] if ok { return true } return false}//手動銷毀session,同時刪除cookiefunc (m *SessionManager) Destroy(w http.ResponseWriter, r *http.Request) { cookie, err := r.Cookie(m.cookieName) if err != nil || cookie.Value == "" { return } else { m.lock.Lock() defer m.lock.Unlock() sid, _ := url.QueryUnescape(cookie.Value) m.storage.DestroySession(sid) cookie2 := http.Cookie{ MaxAge: 0, Name: m.cookieName, Value: "", Path: "/", Expires: time.Now().Add(time.Duration(0)), } http.SetCookie(w, &cookie2) }}func (m *SessionManager) CookieIsExists(r *http.Request) bool { _, err := r.Cookie(m.cookieName) if err != nil { return false } return true}//開啟每個會話,同時定時調用該方法//到達session最大生命時,且逾時時。回收它func (m *SessionManager) GC() { m.lock.Lock() defer m.lock.Unlock() m.storage.GCSession() //在多長時間後執行匿名函數,這裡指在某個時間後執行GC time.AfterFunc(time.Duration(m.maxAge*10), func() { m.GC() })}//是否將session放入記憶體(操作記憶體)預設是操作記憶體func (m *SessionManager) IsFromMemory() { m.storage = newFromMemory()}//是否將session放入資料庫(操作資料庫)func (m *SessionManager) IsFromDB() { //TODO //關於存資料庫暫未實現}func (m *SessionManager) SetMaxAge(t int64) { m.maxAge = t}//如果你自己實現儲存session的方式,可以調該函數進行定義func (m *SessionManager) SetSessionFrom(storage Storage) { m.storage = storage}//產生一定長度的隨機數func (m *SessionManager) randomId() string { b := make([]byte, 32) if _, err := io.ReadFull(rand.Reader, b); err != nil { return "" } //加密 return base64.URLEncoding.EncodeToString(b)
以上封裝的方法還有類似的 具體參考https://www.cnblogs.com/chevin/p/5669940.html
540 次點擊 ∙ 1 贊