简介

上一篇文章《Go 每日一库之 securecookie》中,我们介绍了 cookie。同时提到 cookie 有两个缺点,一是数据不宜过大,二是安全问题。session 是服务器端的存储方案,可以存储大量的数据,而且不需要向客户端传输,从而解决了这两个问题。

但是 session 需要一个能唯一标识用户的 ID,这个 ID 一般存放在 cookie 中发送到客户端保存,随每次请求一起发送到服务器。cookie 和 session 通常配套使用。

gorilla/sessions是 gorilla web 开发工具包中管理 session 的库。它提供了基于 cookie 和本地文件系统的 session。同时预留扩展接口,可以使用其它的后端存储 session 数据。

sessions

快速使用

本文代码使用 Go Modules。

创建目录并初始化:

gorilla/sessions

现在我们实现在服务器端通过 session 存储一些信息的功能:

/set/readstoresession.NewFilesystemStore()*sessions.FilesystemStoreNewFilesytemStore()hashKeyblockKeysecurecookiesecurecookie
sessionsStore

实现这个接口可以自定义我们存储 session 的位置和格式。

setstore.Get(r, "user")usersessionsstore.Get()*Session
Session.Valuesmap[interface{}]interface{}chan
setValuesstore.Save(r, w, session)
getstore.Get(r, "user")*Sessionnameage

运行:

localhost:8080/setApplication

我们发现 session 的名字会作为 cookie 名发送到客户端,session ID 被保存为 cookie 的值。

localhost:8080/read

FilesystemStore

cookie 存储

sessions
sessions.NewCookieStore()sessions.NewFilesystemStore()

其他部分的代码完全不用修改,运行程序的结果与上面的一致。session 数据保存在 cookie 中,随每次请求由客户端传给服务器。这种方式其实就是之前文章中介绍的 cookie 用法。

记录登录状态

gorilla/mux

首先,我们设计 3 个页面,登录页面,主页面,授权才能访问的 secret 页面。登录页面只需要用户名&密码的输入框和登录按钮即可:

handlers.MethodHandler
Login
html/template
DoLoginUser
UserUser
HomeHandler

最后是 secret 页面:

显示访问了该页面多少次。

SecretHandler

如果没有 session,则重定向到登录页面。反之显示该页面。这里每次成功访问 secret 页面,都会增加计数器,保存在 session 中。

encoding/gobUserUser
localhost:8080

点击去登录,跳转到登录界面,输入用户名和密码:

点击登录,跳转到主页,这时由于记录了登录状态,会显示欢迎 darjun:

点击去隐秘链接:

不停刷新页面,发现访问次数一直累加。

localhost:8080/secret

上面程序有一个缺点,程序重启启动后,就需要重新登录。因为每次启动我们都重新随机 hashKey 和 blockKey,只需要固定这两个值即可实现重启也能保存登录状态。

登录验证类的功能非常适合放在中间件中处理,之前的文章已经介绍过如何编写中间件了,这里就不赘述了。

第三方后端存储

将 session 存储在本地文件系统,不利于水平扩展。一般稍微上点规模的网站,Web 服务器都会部署很多个实例,请求通过 Nginx 之类的反向代理转发到一个后端实例处理。不能保证后面的请求与之前的请求在同一个实例中处理,故 session 一般需要存储在一个公共的地方,例如 redis。

sessionssessions

我们只介绍基于 redis 的后端存储,其他的扩展感兴趣可自行研究。首先安装扩展:

创建一个 redistore 的实例:

参数依次为:

size
network
addr
password
keyPairs
flag

为了运行服务器,我们需要先开启一个 redis-server。redis 的安装就不多说了,在 windows 下,建议使用 chocolatey 安装,chocolatey 类似于 Ubutnu 的 apt-get,Mac 的 brew,非常方便,强烈推荐。

为了演示反向代理的效果,即通过一个地址可以随机访问部署的多个 Web 服务器,我们开启 3 个 Web 服务器。终端1:

终端2:

终端3:

nginx
localhostmysvr
localhost

点击去登录,server1 处理了展示页面的请求:

点击登录,server3 处理了 POST 类型的登录请求:

登录成功之后,重定向到主界面的请求又是 server1 处理的:

点击私密链接,展示页面的请求是 server2 处理的:

虽然每次处理的 server 不同,但是登录状态一直保存着。因为我们使用了 redis 保存 session。

注意,我这里每次都是随机一个 server 去处理,你运行的结果不一定一样。

总结

sessions

大家如果发现好玩、好用的 Go 语言库,欢迎到 Go 每日一库 GitHub 上提交 issue😄

参考