go语言网络编程之session的实现

2020-07-09 00:00:00 创建 语言 专区 信息 我们可以

网络编程中cookie和session是必不可少的,今天就简单说一下go语言对session的实现。


图片来源于网络

cookie,简而言之就是在本地计算机保存一些用户操作的历史信息(当然包括登录信息),并在用户再次访问该站点时浏览器通过HTTP协议将本地cookie内容发送给服务器,从而完成验证,或继续上一步操作。

session在Web开发环境下,它的含义是指一类用来在客户端与服务器端之间保持状态的解决方案。有时候Session也用来指这种解决方案的存储结构。

简而言之,cookie是本地的文件,存储一下和服务器相关的内容,session的服务端存储的数据,用来和cookie相互对应,简化用户登录等,因为http都是无状态的,在用户登录后,再访问其他页面时不应该再次登录,所以可以使用cookie和session来解决,我们不可能每一次访问界面都要将用户的账号密码等信息都发送一次,这样不仅是做无用功,而且也很危险。

下面是我用go语言实现的一个简单的session:

package main
import (
 "crypto/rand"
 "encoding/base64"
 "errors"
 "io"
 "net/http"
 "net/url"
 "strconv"
 "sync"
 "time"
)
// SessionMgr session manager
type SessionMgr struct {
 cookieName string
 mLock sync.RWMutex
 maxLifeTime int64
 sessions map[string]*Session
}
// Session
type Session struct {
 sessionID string
 lastTime time.Time
 values map[interface{}]interface{}
}
// NewSessionMgr create session manager
func NewSessionMgr(cookieName string, maxLifeTime int64) *SessionMgr {
 mgr := &SessionMgr{cookieName: cookieName, maxLifeTime: maxLifeTime, sessions: make(map[string]*Session)}
 go mgr.SessionGC()
 return mgr
}
// NewSession create session
func (mgr *SessionMgr) NewSession(w http.ResponseWriter, r *http.Request) string {
 mgr.mLock.Lock()
 defer mgr.mLock.Unlock()
 newSessionID := url.QueryEscape(mgr.NewSessionID())
 session := &Session{sessionID: newSessionID, lastTime: time.Now(),
 values: make(map[interface{}]interface{})}
 mgr.sessions[newSessionID] = session
 cookie := http.Cookie{Name: mgr.cookieName, Value: newSessionID,
 Path: "/", HttpOnly: true, MaxAge: int(mgr.maxLifeTime)}
 http.SetCookie(w, &cookie)
 return newSessionID
}
// EndSession
func (mgr *SessionMgr) EndSession(w http.ResponseWriter, r *http.Request) {
 cookie, err := r.Cookie(mgr.cookieName)
 if err != nil || cookie.Value == "" {
 return
 }
 mgr.mLock.Lock()
 defer mgr.mLock.Unlock()
 delete(mgr.sessions, cookie.Value)
 newCookie := http.Cookie{Name: mgr.cookieName,
 Path: "/", HttpOnly: true,
 Expires: time.Now(), MaxAge: -1}
 http.SetCookie(w, &newCookie)
}
// EndSessionByID end the session by session ID
func (mgr *SessionMgr) EndSessionByID(sessionID string) {
 mgr.mLock.Lock()
 defer mgr.mLock.Unlock()
 delete(mgr.sessions, sessionID)
}
// SetSessionValue set value fo session
func (mgr *SessionMgr) SetSessionValue(sessionID string, key interface{}, value interface{}) error {
 mgr.mLock.Lock()
 defer mgr.mLock.Unlock()
 if session, ok := mgr.sessions[sessionID]; ok {
 session.values[key] = value
 return nil
 }
 return errors.New("invalid session ID")
}
// GetSessionValue get value fo session
func (mgr *SessionMgr) GetSessionValue(sessionID string, key interface{}) (interface{}, error) {
 mgr.mLock.RLock()
 defer mgr.mLock.RUnlock()
 if session, ok := mgr.sessions[sessionID]; ok {
 if val, ok := session.values[key]; ok {
 return val, nil
 }
 }
 return nil, errors.New("invalid session ID")
}
//CheckCookieValid check cookie is valid or not
func (mgr *SessionMgr) CheckCookieValid(w http.ResponseWriter, r *http.Request) (string, error) {
 cookie, err := r.Cookie(mgr.cookieName)
 if cookie == nil ||
 err != nil {
 return "", err
 }
 mgr.mLock.Lock()
 defer mgr.mLock.Unlock()
 sessionID := cookie.Value
 if session, ok := mgr.sessions[sessionID]; ok {
 session.lastTime = time.Now()
 return sessionID, nil
 }
 return "", errors.New("invalid session ID")
}
// SessionGC maintain session
func (mgr *SessionMgr) SessionGC() {
 mgr.mLock.Lock()
 defer mgr.mLock.Unlock()
 for id, session := range mgr.sessions {
 if session.lastTime.Unix()+mgr.maxLifeTime < time.Now().Unix() {
 delete(mgr.sessions, id)
 }
 }
 time.AfterFunc(time.Duration(mgr.maxLifeTime)*time.Second, func() {
 mgr.SessionGC()
 })
}
// NewSessionID generate unique ID
func (mgr *SessionMgr) NewSessionID() string {
 b := make([]byte, 32)
 if _, err := io.ReadFull(rand.Reader, b); err != nil {
 nano := time.Now().UnixNano()
 return strconv.FormatInt(nano, 10)
 }
 return base64.URLEncoding.EncodeToString(b)
}

相关文章