computer-network
  • 简介
  • 应用层
    • HTTP
      • Cookie
      • Session
    • HTTPS
      • 简介
      • 原理
      • RSA加密与解密
      • 证书签名与验证
      • TLS双向认证
  • 传输层
    • TCP
      • CloseWait
      • tcp_tw_recycle与tcp_timestamps
  • 网络层
    • IPv6
  • 链路层
    • VLAN
Powered by GitBook
On this page
  • 简介
  • 示例
  • session.go
  • 实验效果
  • 代码详细解析
  • 代码中各种结构体及函数的源码
  • 参考

Was this helpful?

  1. 应用层
  2. HTTP

Session

简介

在上一篇文章中介绍了Cookie的内容。在HTTP Request中,可以在Header中携带Cookie给服务端;在HTTP Response中,可以在Header中携带Cookie返回给客户端。但是,Cookie的结构体一般是固定的,携带的信息有限。此时,我们可以在服务端保存客户端的相关信息,然后客户端请求时,只需要携带一个Id给服务端,服务端根据此Id查询客户端相关的信息,这就是Session的基本机制。

这里列出了Session与Cookie的几个区别:

  • Cookie是通过HTTP协议的请求头中的Cookie字段与响应头中的Set-Cookie字段进行传输的

  • Cookie的结构体是固定的,所携带的信息有限

  • Cookie由服务端生成,返回给客户端,客户端请求时,再携带给服务端。所以,Cookie是一种将状态信息存储在客户端的机制。

  • Session机制是将状态信息存储在服务端,然后通过Cookie把SessionId传回给客户端

  • Session的结构是不固定的,用户可以自已定义,存储的信息量比Cookie要多

  • Session的后端存储引擎也比较丰富,有memory、file、redis、mysql等。

示例

接下来,我们通过一个例子(go语言)来展示一下session的使用。解释一下这段代码的意思:

  • 在浏览器(客户端)上访问 x.x.x.x:8082/login?username=xxx 时,程序(服务端)会新建一个Session,记录登录的用户名,并把SessionId通过Cookie传回给浏览器

  • 在浏览器上访问x.x.x.x:8082/whoami时,浏览器会携带包含SessionId的Cookie给服务端,服务端根据SessionId查询到Session对象,得到用户名,返回给浏览器

  • 在浏览器上访问x.x.x.x:8082/logout时,浏览器会携带包含SessionId的Cookie给服务端,服务端会根据SeesionId删除对应的Session对象,并且告诉浏览器删除对应的Cookie

session.go

package main

import (
    "net/http"

    "github.com/astaxie/beego/session"
)

var globalSessions *session.Manager

func init() {
    sessionConfig := &session.ManagerConfig{
        CookieName:      "gosessionid",
        EnableSetCookie: true,
        Gclifetime:      3600,
        Maxlifetime:     3600,
        Secure:          false,
        CookieLifeTime:  0,
        ProviderConfig:  "./tmp",
    }
    globalSessions, _ = session.NewManager("memory", sessionConfig)
    go globalSessions.GC()
}

func main() {
    http.HandleFunc("/login", Login)
    http.HandleFunc("/logout", Logout)
    http.HandleFunc("/whoami", WhoAmI)

    http.ListenAndServe(":8082", nil)
}

func Login(w http.ResponseWriter, r *http.Request) {
    sess, _ := globalSessions.SessionStart(w, r) // get or generate a Store object, sess is type of Store
    username := sess.Get("username")    // username is type of interface{}

    if username != nil {
        usernameInString, _ := username.(string) // convert interface{} to string
        w.Write([]byte("already logged in as user : " + usernameInString))
    } else {
        queryUsername := r.URL.Query().Get("username")
        if queryUsername == "" {
            w.Write([]byte("query parameter missed : username"))
            globalSessions.SessionDestroy(w, r)
            return
        }
        sess.Set("username", queryUsername)
        w.Write([]byte("logged in successfully as user : " + queryUsername))
    }
}

func Logout(w http.ResponseWriter, r *http.Request) {
    sess, _ := globalSessions.SessionStart(w, r) // get session object from request
    username := sess.Get("username")

    if username == nil {
        w.Write([]byte("logout failed : no user logged in"))
    }
    globalSessions.SessionDestroy(w, r)
    usernameInString, _ := username.(string)
    w.Write([]byte("logout successfully : " + usernameInString))
}

func WhoAmI(w http.ResponseWriter, r *http.Request) {
    sess, _ := globalSessions.SessionStart(w, r) // get session object from request
    username := sess.Get("username")

    if username == nil {
        w.Write([]byte("i don't know who you are : no user logged in"))
        globalSessions.SessionDestroy(w, r)
    } else {
        usernameInString, _ := username.(string)
        w.Write([]byte("You are : " + usernameInString))
    }
}

实验效果

  • 首先登录

首次访问/login?username=peng,服务端会返回一个Cookie给浏览器,包含了SessionId的内容

  • 携带上述的Cookie访问

接着,访问页面 /whoami,此时浏览器会携带Cookie给服务端,服务端根据Cookie中的SessionId查到出对应的Session,然后返回用户名给浏览器

  • 登出系统

访问/logout,浏览器把Cookie携带给服务端后,服务端会删除该SessionId的Session对象,并且告知浏览器出删除这个Cookie

  • 再次访问 /whoami

登出系统后,再次访问/whoami,由于浏览器没有携带SessionId相关的Cookie,所以服务端会报不知道客户端是谁

代码详细解析

  • sess, _ := globalSessions.SessionStart(w, r)

该语句的意思是从HTTP的请求头中读取承载SessionId的Cookie,然后在服务端查找出对应的Session对象,并且将该Cookie继续保存在HTTP的响应头中(http.RespondWriter);如果请求头中并没有相应的Cookie,则新建一个Session对象,并且把SessionId保存在Cookie中然后放到HTTP的响应头中。

  • sess.Set("username", queryUsername)

globalSessions.SessionStart(w, r)返回的Session对象的结构体与具体的后端存储有关,在上面的代码中我们使用的memory存储引擎。详细的类的定义见下面的内容

代码中各种结构体及函数的源码

  • func NewManager(...)

func NewManager(provideName string, cf *ManagerConfig) (*Manager, error)
  • Manager struct

type Manager struct {
    provider Provider
    config   *ManagerConfig
}

// getSid retrieves session identifier from HTTP Request.
// First try to retrieve id by reading from cookie, session cookie name is configurable,
// if not exist, then retrieve id from querying parameters.
//
// error is not nil when there is anything wrong.
// sid is empty when need to generate a new session id
// otherwise return an valid session id.
func (manager *Manager) getSid(r *http.Request) (string, error)

// SessionStart generate or read the session id from http request.
// if session id exists, return SessionStore with this id.
func (manager *Manager) SessionStart(w http.ResponseWriter, r *http.Request) (session Store, err error)

// SessionDestroy Destroy session by its id in http request cookie
func (manager *Manager) SessionDestroy(w http.ResponseWriter, r *http.Request)

// GetSessionStore Get SessionStore by its id
func (manager *Manager) GetSessionStore(sid string) (sessions Store, err error)

// GC Start session gc process.
// it can do gc in times after gc lifetime.
func (manager *Manager) GC()

// SessionRegenerateID Regenerate a session id for this SessionStore who's id is saving in http request.
func (manager *Manager) SessionRegenerateID(w http.ResponseWriter, r *http.Request) (session Store)

// GetActiveSession Get all active sessions count number.
func (manager *Manager) GetActiveSession() int

// Set cookie with https
func (manager *Manager) SetSecure(req *http.Request) bool

// inner function
func (manager *Manager) sessionID() (string, error)

// inner function
func (manager *Manager) isSecure(req *http.Request) bool
  • Store interface

// 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
}
  • MemSessionStore struct

type MemSessionStore struct {
    sid          string                      //session id
    timeAccessed time.Time                   //last access time
    value        map[interface{}]interface{} //session store
    lock         sync.RWMutex
}

// Set value to memory session
func (st *MemSessionStore) Set(key, value interface{}) error

// Get value from memory session by key
func (st *MemSessionStore) Get(key interface{}) interface{}

// Delete in memory session by key
func (st *MemSessionStore) Delete(key interface{}) error

// Flush clear all values in memory session
func (st *MemSessionStore) Flush() error

// SessionID get this id of memory session store
func (st *MemSessionStore) SessionID() string

// SessionRelease Implement method, no used.
func (st *MemSessionStore) SessionRelease(w http.ResponseWriter)

参考

PreviousCookieNextHTTPS

Last updated 5 years ago

Was this helpful?

[1]

https://beego.me/docs/module/session.md