Files
build-web-application-with-…/6.2.md

182 lines
6.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#6.2 Go如何使用session
通过上一小节的介绍我们知道session是服务器端的一种实现用户和服务器之间认证的解决方案那么session到底怎么样实现整个流程的呢Go目前内部没有任何pkg支持session我们这小节将会使用Go来实现session管理和创建
##session创建过程
session的基本原理是服务端为每一个session维护一份会话信息数据而客户端和服务端依靠一个全局唯一的标识来访问会话信息数据。用户访问web应用时服务端程序决定何时创建session创建session可以概括为三个步骤
- 生成全局唯一标识符sessionid
- 开辟数据存储空间。一般会在内存中创建相应的数据结构但这种情况下系统一旦掉电所有的会话数据就会丢失如果是电子商务网站这种事故会造成严重的后果。不过也可以写到文件里甚至存储在数据库中这样虽然会增加I/O开销但session可以实现某种程度的持久化而且更有利于session的共享
- 将session的全局唯一标示符发送给客户端。
问题的关键就在服务端如何发送这个session的唯一标识上。考虑到HTTP协议的定义数据无非可以放到请求行、头域或Body里所以一般来说会有两种常用的方式cookie和URL重写。
1. Cookie
服务端只要设置Set-cookie头就可以将session的标识符传送到客户端而客户端此后的每一次请求都会带上这个标识符由于cookie可以设置失效时间所以一般包含session信息的cookie会设置失效时间为0(会话cookie)即浏览器进程有效时间。至于浏览器怎么处理这个0每个浏览器都有自己的方案但差别都不会太大(一般体现在新建浏览器窗口的时候)
2. URL重写
所谓URL重写顾名思义就是重写URL。试想在返回用户请求的页面之前将页面内所有的URL后面全部以get参数的方式加上session标识符或者加在path info部分等等这样用户在收到响应之后无论点击哪个链接或提交表单都会在再带上session的标识符从而就实现了会话的保持。读者可能会觉得这种做法比较麻烦确实是这样但是如果客户端禁用了cookie的话URL重写将会是首选。
##Go实现session管理
通过上面session创建过程的讲解读者应该对session有了一个大体的认识但是具体到动态页面技术里面又是怎么实现session的呢下面我们将结合session的生命周期lifecycle用Go语言来实现session管理。
###session数据结构
我们知道session管理需要有如下几个元素
- 全局session管理器
- sessionid 全局唯一性
- sessionid对应的存储对象数据结构 我们采用map[key]interface{}来存储
- session过期设置
我们就来定义一个全局的session管理器
type SessionManager struct {
cookieName string //识别cookie
lock sync.Mutex //用来锁
sessions map[string]*session //用来存储
list *list.List //用来做gc
}
Go实现整个的流程应该也是这样的在main包中创建一个全部的session管理器
var globalSessions *SessionManager
//然后在init函数中初始化
func init() {
globalSessions = NewSessions()
}
接下来的例子中创建的session都是放在内存里面的当然也可以放在存储中这个后面会详细的讲解。
接下来我们来定义针对每个用户的session对象
type session strcut{
sid string //session id唯一标示
timeAccessed time.Time //最后访问时间
value map[string]interface{} //session里面存储的值
}
###创建
首先我们要产生一个唯一的sessionid
func (this *SessionManager) sessionId() string {
var n int = 24
b := make([]byte, n)
io.ReadFull(rand.Reader, b)
return base64.URLEncoding.EncodeToString(b)
}
那么这个sessionid什么时候创建呢我们在启动我们的处理器的时候来进行session的创建这里我继续前面表单处理的例子说明
func login(w http.ResponseWriter, r *http.Request) {
fmt.Println("method:", r.Method) //获取请求的方法
if r.Method == "GET" {
t, _ := template.ParseFiles("login.gtpl")
t.Execute(w, nil)
} else {
//请求的是登陆数据,那么执行登陆的逻辑判断
fmt.Println("username:", r.Form["username"])
fmt.Println("password:", r.Form["password"])
}
}
//开始一个新的sessionid
//先通过cookiename查询是否已经中了sessionid然后查找这个sessionid是否还在sessions里面
//如果还在那么就更新timeAccessed如果不存在就addnew一个
func (this *SessionManager) SessionStart() {
}
//销毁当前sessionid
func (this *SessionManager) SessionDestroy() {
}
//新增一个session
func (this *SessionManager) AddNew(key string, value map[string]interface{}) {
newsid :=this.sessionId()
newsess:=&session{"sid":newsid,"timeAccessed":time.Now(),"value":value}
this.sessions[key]=newsess
this.list.Push(newsess)
}
###保持
###操作值:设置、读取和删除
session对象需要有设置和删除操作
func (this *session) Set(key string, value interface{}){
}
func (this *session) Get(key string){
}
func (this *session) Del(key string){
}
当我们设置完值之后需要去更新相应的sessionmanager里面的值
func (this *SessionManager) update(element *list.Element, value map[string]interface{}) {
this.lock.Lock()
defer this.lock.Unlock()
element.Value.(*session).value = value
this.moveToFront(element)
}
###如何实时更新时间
我们的session是有一定的时间限制的那么如果用户一直在访问页面那么我们需要实时的去更新这个时间如何来做这一步呢请看下面的函数处理
func (this *SessionManager) moveToFront(element *list.Element) {
this.lock.Lock()
defer this.lock.Unlock()
this.list.MoveToFront(element)
element.Value.(*session).timeAccessed = time.Now()
}
###销毁
我们来看一下Go如何来管理销毁,我们启动一个单独的goroutine来做session的销毁
func GC() {
for true {
time.Sleep(1e9 * 60)
globalSessions.gc()
}
}
func init() {
go GC()
}
func (this *SessionManager) gc() {
this.lock.Lock()
defer this.lock.Unlock()
var expire int64 = 60 * 15 // 15 minutes
// back to front
for {
element := this.list.Back()
if element == nil {
break
}
if (element.Value.(*session).timeAccessed.Unix() + expire) < time.Now().Unix() {
this.list.Remove(element)
delete(this.table, element.Value.(*session).sid)
} else {
break
}
}
}
##总结
## links
* [目录](<preface.md>)
* 上一节: [session和cookie](<6.1.md>)
* 下一节: [预防session劫持](<6.3.md>)
## LastModified
* $Id$