- HTTP无状态协议
HTTP本身是一种无状态的连接协议,因此每个请求或响应都是独立的。服务端每次处理完一个客户端请求之后就会断开连接,所以每次请求或响应与之前和之后的请求或响应是没有任何关系的。换句话说,HTTP自身不具备保存之前发送过的请求或响应的状态。
由于HTTP无状态的特性,在Web应用中跟踪用户状态的方法可分为四种:
- 建立包含有跟踪数据的隐藏字段
- 重写包含额外参数的URL
- 使用持续的Cookie
- 使用服务端的Session
Cookie和Session是Web应用比较常见的概念,它们的目的都是为了克服HTTP协议的无状态问题。
Cookie
Cookie是一小段由客户端保存在本地的文本文件,可记录用户ID、密码、浏览过的网页、浏览时间等信息。当客户端再次访问同一个网站时,客户端会把请求连同Cookie一起发送给服务端,服务端通过检查Cookie以辨认用户状态。
Cookie机制
Cookie是由服务端生成后发送给User-Agent(比如浏览器),User-Agent会将Cookie的key/value
保存到本地磁盘上指定目录下的文本文件内。当下次请求相同网站时会发送该Cookie给i服务器,不过前提是浏览器必须提前设置了启用Cookie。
Cookie的key/value
键和值可以由服务端自行定义,这样服务端可知道对应用户是否合法以及是否需要重新登录等。服务器可设置或读取Cookie中包含的信息,借此来维护用户更服务端会话的状态。
Cookie是HTTP协议头的一部分,用户浏览器和服务器之间传递数据。当客户端第一次向服务器发起请求时,服务端会创建一个Cookie,并通过HTTP响应头中的Set-Cookie
属性将Cookie信息返回给客户端,并通知客户端保存。
Cookie的传递流程
- 客户端首次发送请求,此时请求报文中没有Cookie信息。
GET /index HTTP/1.1
Host: www.baidu.com
- 服务端收到请求后生成Cookie信息,同时发送响应报文。
HTTP/1.1 200 OK
Server: Apache
<Set-Cookie: sid=57111807181018; path=/;expires=Web,10-OCT-12 07:12:20 GMT>
Content-Type: text/plain; charset=UTF-8
- 客户端再次向服务端发起请求报文,此时会自动发送保存的Cookie信息。
GET /image/ HTTP1.1
HOST: www.baidu.com
Cookie: sid=57111807181018
Cookie中主要包含NAME(名称)、path(路径)、domain(域名)、expires(有效期)、max-age(过期时间)等属性。
Cookie是针对单个域名domain
的,因此不同域名之间的Cookie是相互独立的。
通过设置Cookie的maxAge属性可以设置Cookie的过期时间,若不设置maxAge则被成为会话Cookie,会话Cookie的生命周期从浏览器打开到关闭为止,主要关闭浏览器窗口,会话Cookie就会消失。会话Cookie一般保存在内存而非磁盘。
通过设置过期时间(setMaxAge(606024)),浏览器会将Cookie保存在本地磁盘中,对于关闭后重新打开的浏览器,这些Cookie依旧有效。
Cookie过期时间的设置方式
cookie.setMaxAge(0); //不记录Cookie
cookie.setMaxAge(-1);//会话级Cookie,关闭浏览器后立即失效。
cookie.setMaxAge(60 * 60);//过期时间设置为1小时
Go Cookie
Go语言标准库net/http
中定义了Cookie的数据结构,用于表示一个出现在HTTP响应头Set-Cookie
的值或HTTP请求头中Cookie的值。
Cookie的数据结构
type Cookie struct{
Name string
Value string
Path string
Domain string
Expires time.Time
RawExpires string
MaxAge int
Secure bool
HttpOnly bool
Raw string
Unparsed []string
}
字段 | 描述 |
---|---|
Expires | Cookie过期时间,使用绝对时间,比如2020/12/21 00:00:00。 |
MaxAge | Cookie的最大生存秒数时长,使用相对时间,比如300秒。 |
Secure | Cookie是否需要安全传输,当为真时表示只有HTTPS才会传输该Cookie。 |
HttpOnly | Cookie是否能够被JavaScripit读取其值 |
Unparsed | 表示未解析的键值对的原始文本 |
没有设置Expires字段的Cookie称为会话Cookie或临时Cookie,这种Cookie在浏览器窗口关闭时会自动删除。设置了Expires字段的Cookie称为持久Cookie,这种Cookie会一直存在,直到指定时间到期或手动删除。
Expires和MaxAge都可以用于设置Cookie的过期时间,Expires字段设置的Cookie在指定时间点过期,MaxAge字段设置的是Cookie自创建之后能够存活多少秒。虽然HTTP 1.1中废弃了Expires并推荐使用MaxAge代替,但几乎所有浏览器仍然支持Expires。微软的IE6/IE7/IE8并不支持MaxAge。为了更好地移植性,优先推荐使用Expires。
最大存活期MaxAge
取值范围
MaxAge | 描述 |
---|---|
0 | 表示尚未设置Max-Age属性 |
<0 | 表示立即删除Cookie,等价于Max-Age:0 。 |
>0 | 表示存在Max-Age属性,且单位为秒。 |
获取Cookie
Request对象中拥有两个获取Cookie的方法和一个添加Cookie的方法
- 解析并返回请求的Cookie头设置的所有Cookie
func (r *Request) Cookies() []*Cookie
- 返回请求中名为
name
的Cookie,若未找到则会返回nil
和ErrNoCookie
。
func (r *Request) Cookie(name string) (*Cookie, error)
例如:
cookie := http.Cookie{Name:"username", Value:"junchow", Expires:expiration}
- 添加Cookie可通过
AddCookie
方法向请求中添加一个Cookie
func (r *Request) AddCookie(c *Cookie)
设置Cookie
Go语言中设置Cookie是通过net/http
标准包中的SetCookie
函数实现的,它会在responseWriter
的头域中添加Set-Cookie
头,其值为cookie
。
http.SetCookie(responseWriter, &cookie)
例如:在HTTP响应中通过设置Set-Cookie
头新增Cookie并将其发送给客户端浏览器
package main
import (
"fmt"
"net/http"
"time"
)
func indexHandler(responseWriter http.ResponseWriter, request *http.Request) {
//获取Cookie
cookie,err := request.Cookie("sessionid")
if cookie == nil && err != nil {
expires := time.Now().Add(time.Hour)
//设置Cookie
cookie := &http.Cookie{
Name:"sessionid",
Value:"1jl3dndo0ex82kal",
MaxAge:60*60,
Expires: expires,
Domain:"127.0.0.1:9090",
Path:"/",
HttpOnly:true,
}
http.SetCookie(responseWriter, cookie)
}else{
//cookie:(*http.Cookie)(nil), err:http: named cookie not present
fmt.Printf("cookie:%#v, err:%v\n", cookie, err)
}
responseWriter.Write([]byte("hello"))
}
func main() {
http.HandleFunc("/", indexHandler)
http.ListenAndServe("0.0.0.0:9090", nil)
}
查看请求
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,zh-TW;q=0.7
Cache-Control: max-age=0
Connection: keep-alive
Cookie: sessionid=1jl3dndo0ex82kal
Host: 127.0.0.1:9090
sec-ch-ua: "Chromium";v="88", "Google Chrome";v="88", ";Not A Brand";v="99"
sec-ch-ua-mobile: ?0
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: none
Sec-Fetch-User: ?1
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.104 Safari/537.36
例如:直接通过HTTP响应头Set-Cookie
来设置Cookie
package main
import (
"fmt"
"net/http"
"net/url"
)
func indexHandler(responseWriter http.ResponseWriter, request *http.Request) {
cookie := http.Cookie{
Name:"account",
Value:url.QueryEscape("junchow"),
HttpOnly:true,
}
responseWriter.Header().Add("Set-Cookie", cookie.String())
fmt.Fprintln(responseWriter, "hello world")
}
func main() {
http.HandleFunc("/", indexHandler)
http.ListenAndServe("0.0.0.0:9090", nil)
}
查看HTTP响应头
Content-Length: 12
Content-Type: text/plain; charset=utf-8
Date: Fri, 22 Jan 2021 19:44:31 GMT
Set-Cookie: account=junchow; HttpOnly
网友评论