上一篇文章《Go 每日一库之 securecookie》中,我们介绍了 cookie。同时提到 cookie 有两个缺点,一是数据不宜过大,二是安全问题。session 是服务器端的存储方案,可以存储大量的数据,而且不需要向客户端传输,从而解决了这两个问题。但是 session 需要一个能唯一标识用户的 ID,这个 ID 一般存放在 cookie 中发送到客户端保存,随每次请求一起发送到服务器。cookie 和 session 通常配套使用。
gorilla/sessions
是 gorilla web 开发工具包中管理 session 的库。它提供了基于 cookie 和本地文件系统的 session。同时预留扩展接口,可以使用其它的后端存储 session 数据。
本文先介绍sessions
提供的两种 session 存储方式,然后通过第三方扩展介绍在多个 Web 服务器实例间如何保持登录状态。
本文代码使用 Go Modules。
创建目录并初始化:
$ mkdir gorilla/sessions && cd gorilla/sessions
$ go mod init github.com/darjun/go-daily-lib/gorilla/sessions
安装gorilla/sessions
库:
$ go get -u github.com/valyala/gorilla/sessions
现在我们实现在服务器端通过 session 存储一些信息的功能:
package main
import (
"fmt"
"github.com/gorilla/mux"
"github.com/gorilla/sessions"
"log"
"net/http"
"os"
)
var (
store = sessions.NewFilesystemStore("./", securecookie.GenerateRandomKey(32), securecookie.GenerateRandomKey(32))
)
func set(w http.ResponseWriter, r *http.Request) {
session, _ := store.Get(r, "user")
session.Values["name"] = "dj"
session.Values["age"] = 18
err := sessions.Save(r, w)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
fmt.Fprintln(w, "Hello World")
}
func read(w http.ResponseWriter, r *http.Request) {
session, _ := store.Get(r, "user")
fmt.Fprintf(w, "name:%s age:%d\n", session.Values["name"], session.Values["age"])
}
func main() {
r := mux.NewRouter()
r.HandleFunc("/set", set)
r.HandleFunc("/read", read)
log.Fatal(http.ListenAndServe(":8080", r))
}
整个程序逻辑比较清晰,分别在/set
和/read
路径下挂上设置和读取的处理函数。重点是变量store
。我们调用session.NewFilesystemStore()
方法创建了一个*sessions.FilesystemStore
类型的对象,它会将我们的 session 内容存储到文件系统(即本地磁盘上)。我们需要给NewFilesytemStore()
方法传入至少 2 个参数,第一个参数指定 session 存储的本地磁盘路径。后续参数依次指定hashKey
和blockKey
(可省略),前者用于验证,后者用于加密,我们可以使用securecookie
生成足够随机的 key,详情见前一篇介绍securecookie
的文章。
sessions
为所有的 session 存储抽象了一个接口Store
:
type Store interface {
Get(r *http.Request, name string) (*Session, error)
New(r *http.Request, name string) (*Session, error)
Save(r *http.Request, w http.ResponseWriter, s *Session) error
}
实现这个接口可以自定义我们存储 session 的位置和格式。
在set
处理函数中,我们调用store.Get(r, "us