[TOC]
## **简介**
在上一篇文章中介绍了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的内容
![](https://img.kancloud.cn/b2/cf/b2cf9e15e153f20a1e41d85684baa330_635x496.png)
* 携带上述的Cookie访问
接着,访问页面 `/whoami`,此时浏览器会携带Cookie给服务端,服务端根据Cookie中的SessionId查到出对应的Session,然后返回用户名给浏览器
![](https://img.kancloud.cn/63/9b/639b527d7ae71e9fa2d9f2c1c700ef52_409x106.png)
* 登出系统
访问 `/logout`,浏览器把Cookie携带给服务端后,服务端会删除该SessionId的Session对象,并且告知浏览器出删除这个Cookie
![](https://img.kancloud.cn/a6/c2/a6c256e565643063568efd4e61f1a9af_600x485.png)
* 再次访问 /whoami
登出系统后,再次访问`/whoami`,由于浏览器没有携带SessionId相关的Cookie,所以服务端会报不知道客户端是谁
![](https://img.kancloud.cn/e2/e3/e2e3dd01f8359017692f01d011e3c38f_382x120.png)
#### **代码详细解析**
* `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)
```
## **参考**
[1] https://beego.me/docs/module/session.md
- 应用层
- HTTP
- Cookie
- Session
- HTTP报文格式
- HTTP的Header字段
- HTTPS
- 简介
- 原理
- RSA加密与解密
- 证书签名与验证
- TLS双向认证
- openssl命令汇总
- DNS
- DNS的记录类型
- DNS的报文格式
- FAQ
- 传输层
- TCP
- CloseWait
- 网络层
- IPv6
- 链路层
- 链接层基础知识
- VLAN
- FAQ
- Linux网络收发包
- 网卡收包
- 网卡发包
- 收发包FAQ
- LVS
- 安装-DR模式
- 基本原理
- Ipvsadm命令
- Netfilter
- Netfilter简介
- 注册钩子函数
- Netfilter中数据包流向
- Iptables的数据结构
- 连接跟踪
- 初识连接跟踪
- 连接跟踪详解
- 连接跟踪数据结构
- 数据包与连接的状态
- NAT
- IPVS
- KubeProxy的IPVS模式
- Linux虚拟网络设备
- 虚拟网络设备简介
- Tap
- VethPair
- Vlan
- Vxlan
- Flannel的VXLAN原理
- Openstack的VXLAN原理
- VXLAN总结
- Bridge
- 给容器设置主机网段IP
- Macvlan
- Ipvlan
- IPIP
- IPIP使用介绍
- IPIP源码分析
- Limdiag网络
- 详细设计
- kubeovn
- IP命令
- Calico
- Calico常见问题
- ARP无响应
- 其他