1. 引言
本文将从Web应用程序处理请求时需要用户信息,同时HTTP又是无状态协议这个矛盾点出发。从该问题出发,简单描述了解决该问题的Token
机制,进而引出Cookie
的实现方案。
基于此我们将详细描述Cookie
的规范,然后详细描述具体的实现方式,进一步描述Gin
框架对Cookie
操作提供的API
,最终提供了一个详细的代码实现。
我们还将详细描述Gin
框架提供API
的实现原理,帮助用户更好得使用这两个API
。
2. 问题引入
在 如何使用Gin搭建一个Go Web应用程序 一文中,我们已经了解了如何使用Gin
搭建一个简单的Web应用程序。然而,在现实的Web应用程序中,大部分功能都是需要用户的身份信息才能处理。举例来说,在一个视频网站查看用户最近观看记录,如果缺少用户身份信息,此时将无法对请求进行处理。
但是HTTP协议的设计,是无状态的,也就是每次请求都是独立的。基于此,应该有一套机制,能够在用户身份认证成功后,给用户分配一个Token
,后续用户在每次请求时,都携带上该Token
,使得服务器能够从请求中获取用户信息,解决HTTP无状态问题。大概流程如下:
上面流程中,需要服务端按照某个协议,向客户端返回Token
;客户端通过该协议,成功解析出服务端返回的Token
,然后在每次请求中携带该Token
。然后服务器端再根据协议,从中解析出Token
信息,获取请求用户信息。
当前常用的有Cookie
,Jwt
,OAuth2.0
等标准,其各有优缺点。其中Cookie
是一种存储在客户端浏览器中的数据。服务端可以通过设置HTTP响应头将Token
存储在Cookie
当中,并在后续请求中从Cookie
中读取Token
。而JWT
则是一种基于JSON格式的安全令牌,可用于在客户端和服务端之间传递信息。
之前,我们在 一文读懂Cookie 中,已经了解Cookie
的相关内容。基于此,我们这次使用Cookie
来实现上述所说的流程,按照Cookie
的规范来实现Token
的返回和请求中Token
的解析。
3. 实现
3.1 Cookie规范说明
这里我们对HTTP协议中的Cookie
规范再补充一下,这里分为两部分,第一部分是服务端如何向客户端发送 Cookie
,第二部分是客户端向服务端发送请求时如何携带Cookie
信息。
对于服务端向客户端发送Cookie
的手段,HTTP协议存在一个Set-Cookie
的头部字段,服务器可以通过Set-Cookie
头部字段将Cookie
发送给客户端。例如下面这个例子:
Set-Cookie: username=abc; expires=Wed, 09 Jun 2023 10:18:14 GMT; path=/
在这个例子中,服务器设置了一个名为username
的Cookie
,它的值是abc
,过期时间是2023年6月9日,路径为/
。浏览器在接收到该Cookie
时,便将其保存起来。
客户端请求时携带Cookie
的方式,则是通过HTTP协议中的Cookie
头部字段,客户端可以通过该头部字段携带信息给服务器端,比如下面这个例子:
Cookie: sessionid=1234
在这个例子中,HTTP请求中携带了一个name
为sessionid
,value
为 1234
的 Cookie
。当服务器端接收到该HTTP
请求后,从中解析出Cookie
的信息,然后基于此实现后续的流程。
3.2 实现说明
回看上述流程,主要分为两个大部分: 客户端和服务器端。在客户端部分,关键任务包括保存浏览器返回的Cookie
信息以及在请求时携带Cookie
信息给服务器。对于服务器端,则是在通过身份校验之后,能够按照规范客户端返回Cookie
,并在接收到请求时,能够正确解析出请求中的 Cookie
信息,识别出用户信息。
对于客户端部分,在浏览器接收到HTTP响应时,如果响应体中有Set-Cookie
头部字段,浏览器会自动保存Cookie
信息;客户端发起请求时,需要将 Cookie
信息传递给服务器。此时浏览器会自动携带通过校验的Cookie
。如果通过校验,此时会在HTTP请求头中携带Cookie
信息给服务端,下面是一个大概的校验流程:
在整个流程中,客户端保存Token
信息和在请求时携带Token
信息这两部分工作,浏览器已经帮我们实现了。剩下的工作集中在服务端的,主要涉及按照Cookie
的规范给客户端返回用户标识,并在接收到客户端请求时从HTTP请求中读取Cookie
以获取到用户的信息。与Cookie
相关的详细内容可以参考文章一文读懂Cookie。
因此下面我们需要做的两件事情,其一,服务器需要按照Cookie
的规范往客户端发送Cookie
的内容;其次,服务器在处理请求时,需要从HTTP请求头中读取出Cookie
的信息,成功识别用户身份。
Gin
框架中提供了一些API
,能够帮助我们在服务端,按照Cookie
规范给客户端发送Cookie
信息,同时也有API
能够帮助我们解析Cookie
的信息。下面我们先来了解相关的API
,然后再基于这些API
,搭建一个能够自动识别用户信息的 Web
应用程序。
3.3 API说明
3.3.1 SetCookie
gin.Context
对象中的 SetCookie
方法用于向客户端返回响应的同时,在Set-Cookie
头部携带Cookie
信息。下面是该方法的详细说明:
func (c *Context) SetCookie(name, value string, maxAge int, path, domain string, secure, httpOnly bool)
name
:cookie 的名称(必须)。value
:cookie 的值(必须)。maxAge
:cookie 的过期时间,以秒为单位。如果为负数,则表示会话 cookie(在浏览器关闭之后删除),如果为零,则表示立即删除 cookie(可选,默认值为-1)。path
:cookie 的路径。如果为空字符串,则使用当前请求的 URI 路径作为默认值(可选,默认值为空字符串)。domain
:cookie 的域名。如果为空字符串,则不设置域名(可选,默认值为空字符串)。secure
:指定是否仅通过 HTTPS 连接发送 cookie。如果为 true,则仅通过 HTTPS 连接发送 cookie;否则,使用 HTTP 或 HTTPS 连接都可以发送 cookie(可选,默认值为 false)。httpOnly
:指定 cookie 是否可通过 java script 访问。如果为 true,则无法通过 java script 访问 cookie;否则,可以通过 java script 访问 cookie(可选,默认值为 true)。
在处理函数中,通过调用SetCookie
方法,便可以向客户端发送一个HTTP cookie。这里举一个代码示例,来帮助读者更好得理解该API
,下面举一个代码示例,如下:
func main() {
router := gin.Default()
router.GET("/set-cookie", func(c *gin.Context) {
c.SetCookie("user", "john", 3600, "/", "", false, true)
c.Str