Provider和Store接口(session.go)
Store接口定义一系列session存储销毁的规范
// Store contains all data for one session process with specific id.
type Store 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
SessionRelease(w http.ResponseWriter) // release the resource & save data to provider & return the data
Flush() error //delete all data
}
Provider是session存储的提供者,定义session的存储方式
// Provider contains global session methods and saved SessionStores.
// it can operate a SessionStore by its id.
type Provider interface {
SessionInit(gclifetime int64, config string) error
SessionRead(sid string) (Store, error)
SessionExist(sid string) bool
SessionRegenerate(oldsid, sid string) (Store, error)
SessionDestroy(sid string) error
SessionAll() int //get all active session
SessionGC()
}
注册自定义的Provider
实现session和provider中的方法,并且注册..
redis sessionStore的持有value map,保存现有session,session操作都会暂存至values map键值对。同时HttpServer每次请求后都会调用SessionRelease(rw)将数据保存至redis
type SessionStore struct {
p *redis.Pool
sid string
lock sync.RWMutex
values map[interface{}]interface{}
maxlifetime int64
}
func init() {
## 调用session.go中的Register方法
session.Register("redis", redispder)
}
func Register(name string, provide Provider) {
if provide == nil {
panic("session: Register provide is nil")
}
if _, dup := provides[name]; dup {
panic("session: Register called twice for provider " + name)
}
provides[name] = provide
}
session.go中定义了 var provides = make(map[string]Provider) 持有注册的Provider
session流程
- beego.run()
关注initBeforeHTTPRun函数
func Run(params ...string) {
initBeforeHTTPRun()
if len(params) > 0 && params[0] != "" {
strs := strings.Split(params[0], ":")
if len(strs) > 0 && strs[0] != "" {
BConfig.Listen.HTTPAddr = strs[0]
}
if len(strs) > 1 && strs[1] != "" {
BConfig.Listen.HTTPPort, _ = strconv.Atoi(strs[1])
}
}
BeeApp.Run()
}
2、注册系列hooks(hooks.go)
func initBeforeHTTPRun() {
//init hooks
...
AddAPPStartHook(registerSession)
...
//挨个执行每个register函数
for _, hk := range hooks {
if err := hk(); err != nil {
panic(err)
}
}
}
- registerSession(hook.go)
获取session配置项,调用NewManager,得到gobelSessions。gobelSessions即为reids session manager
func registerSession() error {
if BConfig.WebConfig.Session.SessionOn {
var err error
sessionConfig := AppConfig.String("sessionConfig")
conf := new(session.ManagerConfig)
if sessionConfig == "" {
conf.CookieName = BConfig.WebConfig.Session.SessionName
conf.EnableSetCookie = BConfig.WebConfig.Session.SessionAutoSetCookie
conf.Gclifetime = BConfig.WebConfig.Session.SessionGCMaxLifetime
conf.Secure = BConfig.Listen.EnableHTTPS
conf.CookieLifeTime = BConfig.WebConfig.Session.SessionCookieLifeTime
conf.ProviderConfig = filepath.ToSlash(BConfig.WebConfig.Session.SessionProviderConfig)
conf.DisableHTTPOnly = BConfig.WebConfig.Session.SessionDisableHTTPOnly
conf.Domain = BConfig.WebConfig.Session.SessionDomain
conf.EnableSidInHTTPHeader = BConfig.WebConfig.Session.SessionEnableSidInHTTPHeader
conf.SessionNameInHTTPHeader = BConfig.WebConfig.Session.SessionNameInHTTPHeader
conf.EnableSidInURLQuery = BConfig.WebConfig.Session.SessionEnableSidInURLQuery
} else {
if err = json.Unmarshal([]byte(sessionConfig), conf); err != nil {
return err
}
}
if GlobalSessions, err = session.NewManager(BConfig.WebConfig.Session.SessionProvider, conf); err != nil {
return err
}
go GlobalSessions.GC()
}
return nil
}
5、从持有注册provider的map中根据provider name得到Manger
func NewManager(provideName string, cf *ManagerConfig) (*Manager, error) {
provider, ok := provides[provideName]
if !ok {
return nil, fmt.Errorf("session: unknown provide %q (forgotten import?)", provideName)
}
if cf.Maxlifetime == 0 {
cf.Maxlifetime = cf.Gclifetime
}
if cf.EnableSidInHTTPHeader {
if cf.SessionNameInHTTPHeader == "" {
panic(errors.New("SessionNameInHTTPHeader is empty"))
}
strMimeHeader := textproto.CanonicalMIMEHeaderKey(cf.SessionNameInHTTPHeader)
if cf.SessionNameInHTTPHeader != strMimeHeader {
strErrMsg := "SessionNameInHTTPHeader (" + cf.SessionNameInHTTPHeader + ") has the wrong format, it should be like this : " + strMimeHeader
panic(errors.New(strErrMsg))
}
}
err := provider.SessionInit(cf.Maxlifetime, cf.ProviderConfig)
if err != nil {
return nil, err
}
if cf.SessionIDLength == 0 {
cf.SessionIDLength = 16
}
return &Manager{
provider,
cf,
}, nil
}
6、httpServer handler中初始化session
- 检测是否开启session
- 初始化上下文context.input.CruSession
- 调用session.go中的SessionStart(rw, r)。验证context.input.CruSession是否有session,如果没有则生成sessionId,返回cookie
- 最后执行redis session 的SessionRelease(rw)
// Implement http.Handler interface.
func (p *ControllerRegister) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
// session init
if BConfig.WebConfig.Session.SessionOn {
var err error
context.Input.CruSession, err = GlobalSessions.SessionStart(rw, r)
if err != nil {
logs.Error(err)
exception("503", context)
goto Admin
}
defer func() {
if context.Input.CruSession != nil {
context.Input.CruSession.SessionRelease(rw)
}
}()
}
初始化,从cookie中获取sid,如果存在则直接读取,如果不存在则生成sessionId,并放入cookie,加入response
func (manager *Manager) SessionStart(w http.ResponseWriter, r *http.Request) (session Store, err error) {
//从cookie获取sessionId
sid, errs := manager.getSid(r)
if errs != nil {
return nil, errs
}
//如果存在直接读取,调用redis session的SessionRead方法,读取完成后放入sessionStore
if sid != "" && manager.provider.SessionExist(sid) {
return manager.provider.SessionRead(sid)
}
// 如果不存在创建sessionId
sid, errs = manager.sessionID()
if errs != nil {
return nil, errs
}
session, err = manager.provider.SessionRead(sid)
if err != nil {
return nil, err
}
cookie := &http.Cookie{
Name: manager.config.CookieName,
Value: url.QueryEscape(sid),
Path: "/",
HttpOnly: !manager.config.DisableHTTPOnly,
Secure: manager.isSecure(r),
Domain: manager.config.Domain,
}
if manager.config.CookieLifeTime > 0 {
cookie.MaxAge = manager.config.CookieLifeTime
cookie.Expires = time.Now().Add(time.Duration(manager.config.CookieLifeTime) * time.Second)
}
if manager.config.EnableSetCookie {
http.SetCookie(w, cookie)
}
r.AddCookie(cookie)
if manager.config.EnableSidInHTTPHeader {
r.Header.Set(manager.config.SessionNameInHTTPHeader, sid)
w.Header().Set(manager.config.SessionNameInHTTPHeader, sid)
}
return
}
session.go
func (manager *Manager) getSid(r *http.Request) (string, error) {
//根据配置的cookieName 从cookie中获取sid
cookie, errs := r.Cookie(manager.config.CookieName)
if errs != nil || cookie.Value == "" {
var sid string
if manager.config.EnableSidInURLQuery {
errs := r.ParseForm()
if errs != nil {
return "", errs
}
sid = r.FormValue(manager.config.CookieName)
}
// if not found in Cookie / param, then read it from request headers
if manager.config.EnableSidInHTTPHeader && sid == "" {
sids, isFound := r.Header[manager.config.SessionNameInHTTPHeader]
if isFound && len(sids) != 0 {
return sids[0], nil
}
}
return sid, nil
}
// HTTP Request contains cookie for sessionid info.
return url.QueryUnescape(cookie.Value)
}