0%

session的介绍和golang实战

Session是服务器端使用的一种记录客户端状态的机制,Session在用户第一次访问服务器的时候自动创建。客户端只保存sessionid到cookie中,而不会保存session,关掉浏览器并不会关闭session。

1. session 介绍

1.1 session与cookie的区别

cookie与session最大的区别就是一个是将数据存放在客户端,一个是将数据存放在服务端。

session通信的一般实现形式是通过cookie来实现,与cookie不同的是,session只会保存一个sessionID在客户端,不会像cookie那样将具体的数据保存在客户端,session具体的数据只会保存在服务端上。

1.2 session流程

Session有两个主要的东西,一个是SessionID,一个是存放在服务端对象池中的Session对象。

客户端访问服务端的时候,会先判断这个客户端的请求数据中是否包含有SessionID,如果没有的话,就会认为这个客户端是第一次进行访问。

因为是第一次访问,所以服务端会给客户端在对象池中创建一个Session对象(假设这个会话是需要维持的),并生成出这个对象的SessionID,接着会通过cookie将SessionID响应给客户端,同时会把Session对象放回对象池里。

客户端接收响应数据后会将SessionID存放在本地,下一次再访问服务端的时候就会把SessionID给带上,服务端就能够通过SessionID获得相应的Session对象,Session就是以这样的一个机制维持会话状态的。

1.3 session存储

session数据存储到内存是最佳的选择。最好的解决方案就是使用分布式缓存技术,例如:memcached和redis,将session信息的存储独立出来也是解决 session 同步问题的方法。

1.4 session劫持防范

其中一个解决方案就是sessionID的值只允许cookie设置,同时设置cookie的httponly为true,这个属性是设置是否可通过客户端脚本访问这个设置的cookie。第一可以防止这个cookie被XSS读取从而引起session劫持,第二cookie设置不会像URL重置方式那么容易获取sessionID。

还有一个解决方案就是,我们给session额外设置一个创建时间的值,一旦过了一定的时间,我们销毁这个sessionID,重新生成新的session,这样可以一定程度上防止session劫持的问题。

2. golang 使用 session

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package main

import (
"github.com/gin-contrib/sessions"
"github.com/gin-contrib/sessions/cookie"
"github.com/gin-gonic/gin"
)

func main() {
r := gin.Default()
store := cookie.NewStore([]byte("secret"))
r.Use(sessions.Sessions("mysession", store))

r.GET("/incr", func(c *gin.Context) {
session := sessions.Default(c)
var count int
v := session.Get("count")
if v == nil {
count = 0
} else {
count = v.(int)
count++
}
session.Set("count", count)
session.Save()
c.JSON(200, gin.H{"count": count})
})
r.Run(":8000")
}
  • github.com/gorilla/sessions
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
// sessions.go
package main

import (
"fmt"
"net/http"

"github.com/gorilla/sessions"
)

var (
// key must be 16, 24 or 32 bytes long (AES-128, AES-192 or AES-256)
key = []byte("super-secret-key")
store = sessions.NewCookieStore(key)
)

func secret(w http.ResponseWriter, r *http.Request) {
session, _ := store.Get(r, "cookie-name")

// Check if user is authenticated
if auth, ok := session.Values["authenticated"].(bool); !ok || !auth {
http.Error(w, "Forbidden", http.StatusForbidden)
return
}

// Print secret message
fmt.Fprintln(w, "The cake is a lie!")
}

func login(w http.ResponseWriter, r *http.Request) {
session, _ := store.Get(r, "cookie-name")

// Authentication goes here
// ...

// Set user as authenticated
session.Values["authenticated"] = true
session.Save(r, w)
}

func logout(w http.ResponseWriter, r *http.Request) {
session, _ := store.Get(r, "cookie-name")

// Revoke users authentication
session.Values["authenticated"] = false
session.Save(r, w)
}

func main() {
http.HandleFunc("/secret", secret)
http.HandleFunc("/login", login)
http.HandleFunc("/logout", logout)

http.ListenAndServe(":8080", nil)
}
1
2
3
4
5
6
7
8
9
10
$ go run sessions.go

$ curl -s http://localhost:8080/secret
Forbidden

$ curl -s -I http://localhost:8080/login
Set-Cookie: cookie-name=MTQ4NzE5Mz...

$ curl -s --cookie "cookie-name=MTQ4NzE5Mz..." http://localhost:8080/secret
The cake is a lie!

3. 参考资料

给作者打赏,可以加首页微信,咨询作者相关问题!