cookie
Web开发中一个很重要的议题就是如何做好用户的整个浏览过程的控制,因为HTTP协议是无状态的,所以用户的每一次请求都是无状态的,我们不知道在整个Web操作过程中哪些连接与该用户有关,我们应该如何来解决这个问题呢?Web里面经典的解决方案是cookie和session,cookie机制是一种客户端机制,把用户数据保存在客户端,而session机制是一种服务器端的机制,服务器使用一种类似于散列表的结构来保存信息,每一个网站访客都会被分配给一个唯一的标志符,即sessionID,它的存放形式无非两种:要么经过url传递,要么保存在客户端的cookies里。当然,你也可以将Session保存到数据库里,这样会更安全,但效率方面会有所下降。
一、cookie的介绍
session和cookie是网站浏览中较为常见的两个概念,也是比较难以辨析的两个概念,但它们在浏览需要认证的服务页面以及页面统计中却相当关键。我们先来了解一下cookie怎么来的?考虑这样一个问题:
如何抓取一个访问受限的网页?如新浪微博好友的主页,个人微博页面等。
显然,通过浏览器,我们可以手动输入用户名和密码来访问页面,而所谓的“抓取”,其实就是使用程序来模拟完成
同样的工作,因此我们需要了解“登陆”过程中到底发生了什么。
当用户来到微博登陆页面,输入用户名和密码之后点击“登录”后浏览器将认证信息POST给远端的服务器,服务器执行验证逻辑,如果验证通过,则浏览器会跳转到登录用户的微博首页,在登录成功后,服务器如何验证我们对其他受限制页面的访问呢?因为HTTP协议是无状态的,所以很显然服务器不可能知道我们已经在上一次的HTTP请求中通过了验证。当然,最简单的解决方案就是所有的请求里面都带上用户名和密码,这样虽然可行,但大大加重了服务器的负担(对于每个request都需要到数据库验证),也大大降低了用户体验(每个页面都需要重新输入用户名密码,每个页面都带有登录表单)。既然直接在请求中带上用户名与密码不可行,那么就只有在服务器或客户端保存一些类似的可以代表身份的信息了,所以就有了cookie与session。
cookie,简而言之就是在本地计算机保存一些用户操作的历史信息(当然包括登录信息),并在用户再次访问该站点时浏览器通过HTTP协议将本地cookie内容发送给服务器,从而完成验证,或继续上一步操作。
cookie的原理图:
Cookie是由浏览器维持的,存储在客户端的一小段文本信息,伴随着用户请求和页面在Web服务器和浏览器之间传递。用户每次访问站点时,Web应用程序都可以读取cookie包含的信息。浏览器设置里面有cookie隐私数据选项,打开它,可以看到很多已访问网站的cookies,如下图所示:
cookie是有时间限制的,根据生命期不同分成两种:会话cookie和持久cookie;
如果不设置过期时间,则表示这个cookie生命周期为从创建到浏览器关闭止,只要关闭浏览器窗口,cookie就消失了。这种生命期为浏览会话期的cookie被称为会话cookie。会话cookie一般不保存在硬盘上而是保存在内存里。
如果设置了过期时间(setMaxAge(606024)),浏览器就会把cookie保存到硬盘上,关闭后再次打开浏览器,这些cookie依然有效直到超过设定的过期时间。存储在硬盘上的cookie可以在不同的浏览器进程间共享,比如两个IE窗口。而对于保存在内存的cookie,不同的浏览器有不同的处理方式。
二、Go使用cookie
Go语言中通过net/http包中的SetCookie来设置:
http.SetCookie(w ResponseWriter, cookie *Cookie)
w表示需要写入的response,cookie是一个struct,让我们来看一下cookie对象是怎么样的
type Cookie struct {Name stringValue stringPath string // optionalDomain string // optionalExpires time.Time // optionalRawExpires string // for reading cookies only// MaxAge=0 means no 'Max-Age' attribute specified.// MaxAge<0 means delete cookie now, equivalently 'Max-Age: 0'// MaxAge>0 means Max-Age attribute present and given in secondsMaxAge intSecure boolHttpOnly boolRaw stringUnparsed []string // Raw text of unparsed attribute-value pairs }
我们来看一个例子,如何设置cookie,以及获取cookie
package mainimport ("net/http""fmt" )func main() {http.HandleFunc("/setcookie", SetCookie)http.HandleFunc("/getcookie", GetCookie)http.ListenAndServe("localhost:8080", nil)}func SetCookie(w http.ResponseWriter, r *http.Request) {//设置cookiecookie := http.Cookie{Name: "name", Value: "hanru", Path: "/", MaxAge: 60}http.SetCookie(w, &cookie)w.Write([]byte("write cookie ok"))}//Go读取cookie func GetCookie(w http.ResponseWriter, r *http.Request) {cookie2, _ := r.Cookie("name")fmt.Fprint(w, cookie2)//还有另外一种读取方式//for _, cookie := range r.Cookies() {// fmt.Fprint(w, cookie.Name)//} }
运行服务器后,打开浏览器输入地址:http://127.0.0.1:8080/setcookie
运行结果如下:
在chrome浏览器中执行:http://127.0.0.1:8080/setcookie, 触发go服务执行设置cookie的动作, 浏览器收到这个信息后, 真正执行设置cookie, 在chrome中可查(chrome://settings/content/cookies目录中的localhost域名中查), 如下:
可以看到,过期时间是60s, 60s后再次查的时候, 就没有name对应的cookie项了。
接下来,我们获取一下cookie,要在设置cookie的60s内进行获取,否则就取不到了。
然后打开浏览器,输入网址:http://127.0.0.1:8080/getcookie
运行效果如下:
我们等cookie过期消失, 然后再来玩抓包。 好,现在没有name对应的cookie了, 我们连续两次执行:http://127.0.0.1:8080/setcookie, 服务端tcpdump抓包,两次的请求和响应依次如下:
第一次:
第二次:
可以看到,第一次请求时, 浏览器发出的请求中不带cookie, 因为浏览器压根就没有这项cookie, 在go服务端的回包中携带了Set-Cookie首部, 浏览器收到这个信息后,将其写入到浏览器本地, 形成cookie。
第二次请求时(因cookie的过期时间设置为了60s, 故必须在60s内发起第二次请求), 此时, 浏览器的http请求中自动携带了cookie信息,go服务端也能收到这个信息。
由此可见, cookie不过是浏览器端的一个环境变量而已, 仅此而已。cookie值存在于浏览器所在的本地电脑中。 而所谓的服务端设置cookie, 其实是服务端给浏览器发送对应的设置cookie信息, 由浏览器来真正执行设置cookie的操作, 存于本地电脑磁盘中。
转载自https://www.chaindesk.cn/witbook/17/265,谢谢韩老师