beego 세션 원본 분석

9221 단어

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 등록


세션과provider를 실현하는 방법, 그리고 등록...redis session Store의 보유value 맵은 기존session을 저장하고session 작업은values 맵 키 값에 임시로 저장합니다.동시에 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를 가지고 있음을 정의했습니다.

세션 프로세스

  • 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 설정 항목을 가져오고 New Manager를 호출하여 gobelSessions를 받습니다.gobelSessions는 reids 세션 관리자입니다.
    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를 소지한 맵에서providername에 따라 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 초기화
  • 세션 오픈 여부를 검사합니다
  • 상하문context를 초기화합니다.input.CruSession
  • 세션 호출.go의 SessionStart(rw,r)입니다.context를 검증합니다.input.CruSession에 session이 있는지, 없으면 sessionId를 생성하고 쿠키를 되돌려줍니다
  • 마지막으로 redis 세션을 실행하는 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)
                }
            }()
        }
    

    초기화, 쿠키에서sid를 가져오고, 존재하면 직접 읽고, 존재하지 않으면sessionId를 생성하고, 쿠키를 넣고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)
    }
    

    좋은 웹페이지 즐겨찾기