beego session源码分析

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流程

  1. 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)
        }
    }
}
  1. 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)
}

你可能感兴趣的:(beego session源码分析)