很多变量前面加了个h,有洁癖的小伙伴见谅,现在的版本能够实现用户单一登陆,属于内存版的简单session管理,希望有大神讲一下怎么实现一个浏览器多用户登陆情况下单一登陆限制。
package hsession
import (
"crypto/rand"
"encoding/base64"
"io"
"net/http"
"net/url"
"strconv"
"sync"
"time"
)
// manage all sessions in system running
type HSessionMgr struct {
hCookieName string // client cookie name
hMaxLifeTime int64 // determine whether to delete
hSessions map[string]*HSession // container for sessions
hLock sync.RWMutex // mutex
}
type HSession struct {
hSessionID string //unique key
hLastTimeAccessed time.Time
hValues map[interface{}]interface{} //store variables
}
// create a session manager
// run GC
func NewSessionMgr(cookieName string, maxLifeTime int64) *HSessionMgr {
mgr := &HSessionMgr{hCookieName: cookieName, hMaxLifeTime: maxLifeTime, hSessions: make(map[string]*HSession)}
go mgr.GC()
return mgr
}
//func NewSessionMgr(cookieName string, args []interface{}) *HSessionMgr {
// maxLifeTime :=3600
// mgr := &HSessionMgr{hCookieName: cookieName, hMaxLifeTime: maxLifeTime, hSessions: make(map[string]*HSession)}
// go mgr.GC()
// return mgr
//}
//start session at login action
func (hSessionMgr *HSessionMgr) StartSession(w http.ResponseWriter, r *http.Request) string {
hSessionMgr.hLock.Lock() // add lock
defer hSessionMgr.hLock.Unlock() // auto unlock
newSessionID := url.QueryEscape(hSessionMgr.NewSessionID()) //create new session,whether or not
sessionPoint := &HSession{hSessionID: newSessionID, hLastTimeAccessed: time.Now(), hValues: make(map[interface{}]interface{})}
hSessionMgr.hSessions[newSessionID] = sessionPoint
//set cookie
responseCookie := http.Cookie{Name: hSessionMgr.hCookieName, Value: newSessionID, Path: "/", HttpOnly: true, MaxAge: int(hSessionMgr.hMaxLifeTime)}
http.SetCookie(w, &responseCookie) // write
return newSessionID
}
//stop session 1.drop session object from memory 2.set expire cookie for web
func (hSessionMgr *HSessionMgr) StopSession(w http.ResponseWriter, r *http.Request) {
cookie, err := r.Cookie(hSessionMgr.hCookieName)
if err != nil || cookie.Value == "" { //no cookie value
return
} else { //have cookie value
hSessionMgr.hLock.Lock()
defer hSessionMgr.hLock.Unlock()
delete(hSessionMgr.hSessions, cookie.Value) //drop session's point in session' map
// set cookie timeout right now
expireTime := time.Now()
cookie := http.Cookie{Name: hSessionMgr.hCookieName, Path: "/", HttpOnly: true, Expires: expireTime, MaxAge: -1}
http.SetCookie(w, &cookie)
}
}
//manually stopping the session
func (hSessionMgr *HSessionMgr) StopSessionBySessionID(sessionID string) {
hSessionMgr.hLock.Lock()
defer hSessionMgr.hLock.Unlock()
delete(hSessionMgr.hSessions, sessionID)
}
// set values in the session
func (hSessionMgr *HSessionMgr) SetSessionInnerData(sessionID string, key interface{}, value interface{}) {
hSessionMgr.hLock.Lock()
defer hSessionMgr.hLock.Unlock()
if session, ok := hSessionMgr.hSessions[sessionID]; ok {
session.hValues[key] = value
}
}
// get values in the session
func (hSessionMgr *HSessionMgr) GetSessionInnerData(sessionID string, key interface{}) (interface{}, bool) {
hSessionMgr.hLock.Lock()
defer hSessionMgr.hLock.Unlock()
if session, ok := hSessionMgr.hSessions[sessionID]; ok {
if val, ok := session.hValues[key]; ok {
return val, ok
}
}
return nil, false
}
// get sessionID list
func (hSessionMgr HSessionMgr) GetSessionIDList() []string {
hSessionMgr.hLock.Lock()
defer hSessionMgr.hLock.Unlock()
sessionIDList := make([]string, 0)
for key, _ := range hSessionMgr.hSessions {
sessionIDList = append(sessionIDList, key)
}
return sessionIDList[0:len(sessionIDList)] //len cap
}
//validate cookie using in intercept when get request from client
func (hSessionMgr *HSessionMgr) CheckValidAndFlashTime(w http.ResponseWriter, r *http.Request) (string, bool) {
cookie, err := r.Cookie(hSessionMgr.hCookieName)
if cookie == nil || err != nil {
return "cookie is nil", false
}
hSessionMgr.hLock.Lock()
defer hSessionMgr.hLock.Unlock()
currentSessionID := cookie.Value
if session, ok := hSessionMgr.hSessions[currentSessionID]; ok { //judge if existed
session.hLastTimeAccessed = time.Now() //flash time
return currentSessionID, true
}
return "no session id in memory", false
}
// get last flash time
func (hSessionMgr *HSessionMgr) GetLastAccessedTime(sessionID string) (time.Time, bool) {
hSessionMgr.hLock.Lock()
defer hSessionMgr.hLock.Unlock()
if session, ok := hSessionMgr.hSessions[sessionID]; ok {
return session.hLastTimeAccessed, true
}
return time.Now(), false
}
func (hSessionMgr *HSessionMgr) GC() {
hSessionMgr.hLock.Lock()
defer hSessionMgr.hLock.Unlock()
for sessionID, session := range hSessionMgr.hSessions {
if session.hLastTimeAccessed.Unix()+hSessionMgr.hMaxLifeTime < time.Now().Unix() {
//uid := strconv.FormatInt(session.hValues["uid"].(int64), 10) todo 更新用户状态为离线
delete(hSessionMgr.hSessions, sessionID)
}
}
time.AfterFunc(time.Duration(hSessionMgr.hMaxLifeTime)*time.Second, func() {
hSessionMgr.GC()
})
}
// create unique key
func (hSessionMgr *HSessionMgr) 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)
}
func (hSessionMgr *HSessionMgr) CurrentUserID(r *http.Request) int64 {
//user := model.User{}
cookie, err := r.Cookie(hSessionMgr.hCookieName)
if cookie == nil || err != nil {
return -1
}
session := hSessionMgr.hSessions[cookie.Value]
//service.User.Get(r,uid,&user)
uid := session.hValues["uid"].(int64)
return uid
}
func (hSessionMgr *HSessionMgr) DropByUserID(uid int64) bool {
for key, session := range hSessionMgr.hSessions {
if session.hValues["uid"] == uid {
delete(hSessionMgr.hSessions, key)
return true
}
}
return false
}
var HSessionHelper *HSessionMgr //singleton pattern