内容简介:在Go Web 编程系列的每篇文章的源代码都打了对应版本的软件包,供大家参考。公众号中回复
在 Web
应用开发中 Session
是在用户和服务器之间进行交换的非持久化交互信息。当用户登录时,可以在用户和服务器之间生成 Session
,然后来回交换数据,并在用户登出时销毁 Session
。 gorilla/sessions
软件包提供了易于使用的 Go
语言 Session
实现。该软件包提供了两种不同的实现。第一个是文件系统存储,它将每个会话存储在服务器的文件系统中。另一个是 Cookie
存储,它使用我们 上篇文章 讲的 SecureCookie
在客户端上存储会话。同时还提供了用户自定义 Session
存储实现的选项,我们可以根据应用的需求自己实现 Session
存储。因为我们的教程是学会使用为目的就不大费周章的去实现 MySQL
或者 Redis
版本的 Session
存储了,我们直接使用软件包提供的 Cookie
实现来完成本节的 Session
相关内容。
Go Web 编程系列的每篇文章的源代码都打了对应版本的软件包,供大家参考。公众号中回复 gohttp09
获取本文源代码
使用Cookie存储用户Session的优缺点
客户端使用 Cookie
管理用户 Session
较之在服务器进行用户的 Session
管理会有一些优势。客户端 Session
增加了应用程序的可伸缩性,因为所有的会话数据都存储在用户端,因此可以将用户的请求平衡到不同的远端服务器,也不必在服务器端对所有用户的会话进行统一管理,所以使用 Cookie
存储用户 Session
会更简单一些。
当然有优势就必定有劣势,客户端 Cookie
的整体大小是有限制的。目前, Google Chrome
浏览器将 Cookie
限制为 4096
个字节。
客户端会话还意味着无法终止会话,从而导致注销不完整。如果用户在退出前保存了 Cookie
中的会话信息,则他们可以使用该会话信息创建一个新的 Cookie
,然后继续使用该应用程序,为了最大程度地降低安全风险,我们可以将会话 Cookie
设置为在合理的时间内过期,使用加密后的 ScureCookie
存储数据,同时还要避免在其中存储敏感信息(即使是服务端管理 Session
也不应该存储类似密码这种敏感信息)。
总之在考虑使用客户端还是服务端存储用户 Session
时一定要根据应用的使用场景来选择,这一点很重要。
安装gorilla/sessions
在开始编码前先来安装一下 gorilla/sessions
软件包,
$ go get github.com/gorilla/sessions
并简单看一下软件包功能特性的介绍
- 方便地设置签名(也可以选择加密)的
Cookie
。 - 自带将会话存储在
Cookie
或服务端文件系统中的SessionStore
实现。 - 支持Flash消息:读取即销毁的会话数据。
- 支持方便地切换会话数据的持久化方式。
- 为不同的
Session
存储提供统一的接口和基础设施。
演示用户Session设计实现
我们今天的示例代码是用 gorilla/sessions
提供的 CookieSessionStore
实现一个简单的系统登录功能。
我们会定义如下几个路由:
-
/user/login
用户登录验证,验证成功后在用户Session
数据中标记用户是已验证的。 -
/user/logout
用户登出,会在Session
中标记用户是未认证的。 -
/user/secret
通过用户Session
判断用户是否已认证,未认证返回403 Forbidden
错误。
为了达到演示目的的同时减少文章中出现过多代码,我们不会做前端页面,通过命令行 cURL
直接请求上面几个 URL
验证我们的系统登录功能。
初始化工作
我们现在项目的 handler
目录下新建一个 user
子目录,用于存放使用到用户 Session
的处理程序
... handler/ └── user/ └── init.go └── login.go └── logout.go └── secret.go ... main.go
其下的四个分别是包的初始化程序 init.go
以及存放上面说的三个路由处理程序的 .go
源文件。
初始化Session存储
我们把 Session
存储的初始化工作放在 user
包的 init
函数中,这样首次导入 user
包时即可完成相关的初始化工作。
package user import "github.com/gorilla/sessions" const ( //64位 cookieStoreAuthKey = "..." //AES encrypt key必须是16或者32位 cookieStoreEncryptKey = "..." ) var sessionStore *sessions.CookieStore func init () { sessionStore = sessions.NewCookieStore( []byte(cookieStoreAuthKey), []byte(cookieStoreEncryptKey), ) sessionStore.Options = &sessions.Options{ HttpOnly: true, MaxAge: 60 * 15, } }
实现登录验证
// login.go var sessionCookieName = "user-session" func Login(w http.ResponseWriter, r *http.Request) { session, err := sessionStore.Get(r, sessionCookieName) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } // 登录验证 name := r.FormValue("name") pass := r.FormValue("password") _, err = logic.AuthenticateUser(name, pass) if err != nil { http.Error(w, err.Error(), http.StatusUnauthorized) return } // 在session中标记用户已经通过登录验证 session.Values["authenticated"] = true err = session.Save(r, w) fmt.Fprintln(w, "登录成功!", err) }
- 我们将浏览器
Cookie
中存储用户Session
的Cookie-Name
设置成了user-session
。 - 登录验证就是简单的用户名和密码查找匹配的用户,在之前的文章 应用数据库 和 应用 ORM 两篇文章中有在
MySQL
数据库中创建users
表,并介绍了怎么使用ORM
操作数据库,没有看过的同学可以回看一下。 - 登录验证成功后在
Session
的authenticated
中标记了用户已通过认证。session.Values
是类型map[interface{}]interface{}
的别名,所以可以往其中存储任意类型的数据。
实现登出
登出我们这里就是简单的将 Session
中 authenticated
的值设置成了 false
.
//logout.go func Logout(w http.ResponseWriter, r *http.Request) { session, _ := sessionStore.Get(r, sessionCookieName) session.Values["authenticated"] = false session.Save(r, w) }
使用Session认证用户
//secret.go func Secret(w http.ResponseWriter, r *http.Request) { session, _ := sessionStore.Get(r, sessionCookieName) if auth, ok := session.Values["authenticated"].(bool); !ok || !auth { http.Error(w, "Forbidden", http.StatusForbidden) return } fmt.Fprintln(w, "这里还是空空如也!") }
- 使用
Session
中存储的数据值都是接口类型的,所以使用时要先对其进行类型断言session.Values["authenticated"].(bool)
- 如果
authenticated
的值不为true
或者是从Session
中获取不到对应的值,这里直接返回HTTP 403 Forbidden
错误。
注册路由
// router.go func RegisterRoutes(r *mux.Router) { ... userRouter := r.PathPrefix("/user").Subrouter() userRouter.HandleFunc("/login", user.Login).Methods("POST") userRouter.HandleFunc("/secret", user.Secret) userRouter.HandleFunc("/logout", user.Logout) ... }
验证已实现的Session管理功能
编写完上面的 Session
管理的功能后,重启服务器,然后使用 cURL
分别请求 URL
验证一下效果。
curl -XPOST -d 'name=Klein&password=123' \ -c - http://localhost:8000/user/login
-c
选项表示将 Cookie
写入到后面的文件中,完整格式是 -c -<file_name>
,短横线后不带文件名表示把 Cookie
写入到标准输出中。
我们可以在下图里看到, Cookie
中的 user-session
存储的就是加密后的 Session
数据了
如果请求中不携带这个 Cookie
访问 /user/secret
会直接返回 HTTP 403
错误
那么接下来在使用 cURL
请求 /user/secret
时带上上面返回的 Cookie
值,看看请求是否能成功
curl --cookie "user-session=MTU4m..." http://localhost:8000/user/secret
Cookie
加密后的值太长了,搞得字儿好小, cURL
执行的结果显示服务器成功地响应了我们的请求。你们试验的时候换成自己生成的 Cookie
值请求就可以啦。
你们实践时也可以用 PostMan
代替 cURL
试验,不过感觉 PostMan
的返回不如 cURL
来的明显。
Go Web 编程系列的每篇文章的源代码都打了对应版本的软件包,供大家参考。公众号中回复 gohttp09
获取本文源代码
前文回顾
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 客户端多线程编程解决思路
- python编程(wx客户端编写)
- 使用MVVM尝试开发Github客户端及对编程的一些思考
- 支付宝客户端架构解析:iOS 客户端启动性能优化初探
- 自己动手做数据库客户端: BashSQL开源数据库客户端
- 支付宝客户端架构解析:Android 客户端启动速度优化之「垃圾回收」
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
PHP 5完全攻略
杜江 / 2010-5 / 79.00元
《PHP 5完全攻略(畅销书升级版)》是目前第一本真正介绍PHP 5及MySQL 5新增语法与功能的中文版本权威宝典!《PHP 5完全攻略(畅销书升级版)》本着精、全、要三宗旨,从理论中延伸,从实践中深入,翔实并完善地描述了PHP 5的开发特性与MySQL 5数据库。《PHP 5完全攻略(畅销书升级版)》分为两大部分,第1部分主要阐述PHP开发的基础知识,如PHP数组与表单处理、PHP 5面向对象......一起来看看 《PHP 5完全攻略》 这本书的介绍吧!