一、简介
Web
开发中一个很重要的议题就是如何做好用户在整个浏览过程中的控制,因为HTTP
协议是无状态的,所以用户的每一次请求都是无状态的,我们不知道在整个Web
操作过程中哪些连接与该用户有关,我们应该如何来解决这个问题呢?Web
里面经典的解决方案是cookie
和session
,cookie
机制是一种客户端机制,把用户数据保存在客户端,而session
机制是一种服务器端的机制,服务器使用一种类似于散列表的结构来保存信息,每一个网站访客都会被分配给一个唯一的标志符,即sessionID
,它的存放形式无非两种:要么经过url
传递,要么保存在客户端的cookies
里.当然,你也可以将Session
保存到数据库或者redis
甚至文件里,这样会更安全,但效率方面会有所下降。
该系列涉及如下几个模块:
session
机制和cookie
机制的关系和区别Go
语言实现session
:一个简易的session
管理器session
存储实现:以存于内存为例- 如何防止
session
被劫持
二、session和cookie
1. 认证流程
session
和cookie
是网站浏览中较为常见的两个概念,也是比较难以辨析的两个概念,但它们在浏览需要认证的服务页面以及页面统计中却相当关键。我们先来了解一下session
和cookie
怎么来的?考虑这样一个问题:
如何抓取一个访问受限的网页?如新浪微博好友的主页,个人微博页面等。
显然,通过浏览器,我们可以手动输入用户名和密码来访问页面,而所谓的“抓取”,其实就是使用程序来模拟完成同样的工作,因此我们需要了解“登陆”过程中到底发生了什么。
当用户来到微博登陆页面,输入用户名和密码之后点击“登录”后浏览器将认证信息POST
给远端的服务器,服务器执行验证逻辑,如果验证通过,则浏览器会跳转到登录用户的微博首页,在登录成功后,服务器如何验证我们对其他受限制页面的访问呢?因为HTTP
协议是无状态的,所以很显然服务器不可能知道我们已经在上一次的HTTP
请求中通过了验证。当然,最简单的解决方案就是所有的请求里面都带上用户名和密码,这样虽然可行,但大大加重了服务器的负担(对于每个request
都需要到数据库验证),也大大降低了用户体验(每个页面都需要重新输入用户名密码,每个页面都带有登录表单)。既然直接在请求中带上用户名与密码不可行,那么就只有在服务器或客户端保存一些类似的可以代表身份的信息了,所以就有了cookie
与session
。
cookie,简而言之就是在本地计算机保存一些用户操作的历史信息(当然包括登录信息),并在用户再次访问该站点时浏览器通过HTTP协议将本地cookie内容发送给服务器,从而完成验证,或继续上一步操作。cookie原理图如下:
session,简而言之就是在服务器上保存用户操作的历史信息。服务器使用session id来标识session,session id由服务器负责产生,保证随机性与唯一性,相当于一个随机密钥,避免在握手或传输中暴露用户真实密码。但该方式下,仍然需要将发送请求的客户端与session进行对应,所以可以借助cookie机制来获取客户端的标识(即session id),也可以通过GET方式将id提交给服务器。session原理图如下:
使用cookie和session完成认证功能的流程如下:
- 客户端通过用户名密码/第三方登录进行认证
- 认证成功后,在服务器端生成对应的session,然后将sessionID通过HTTP响应头Cookie发送给客户端
- 客户端访问需要认证的接口时,在Cookie中携带对应的SessionID
- 服务端根据对应的SessionID查找Session并进行鉴权 返回给客户端需要的数据
2. cookie详解
Cookie
是由浏览器维持的,存储在客户端的一小段文本信息,伴随着用户请求和页面在Web
服务器和浏览器之间传递。用户每次访问站点时,Web
应用程序都可以读取cookie
包含的信息。浏览器设置里面有cookie
隐私数据选项,打开它,可以看到很多已访问网站的cookies
,如下图所示:
Name(名称)
cookie
的存储方式类似于 key-value
形式,name
可以理解为 key
,同域下,name
不能重复,否则后者覆盖掉前面的。虽然允许中文存储,但是尽量不要使用中文存储,有的浏览器、有的场景下,会有兼容性问题,导致乱码,name
尽量别是中文。
Value(内容)
cookie
的实际值,如果存储中文的话,需要进行编码,读取的时候再行解码
Domain(域)
cookie
所在的域,cookie
不能跨域写入、读取,cookie
默认存储在本域,也可以存储在本域的父级域名、根域之下
Path(路径)
cookie
存储的路径,如果不做特殊说明的话,cookie
默认存储是当前路径,但是其余路径的界面,就无法读取到这个 cookie
,一般而言,都是存储到根目录,即/目录
Expires/Max-Age(过期时间)
为过期时间/最大时效,如果是显示未来的某个时间点,该条 cookie
会在这个时间点失效,如果是显示为 session
或者 /
,证明该条 cookie 在浏览器关闭之后,就会失效。
Size
cookie
的大小,单位为字节,可以查看哪条cookie
过去庞大,用于优化
HTTP
是否仅用于传输。如果该栏目为勾选状态,说明该条 cookie
是 JS
无法读取、写入的,只能通过服务器进行读写
Secure
是否仅适用于https
,如果此项勾选,该条 cookie
仅在https
下才能生效
SameSite
是否防止跨域发送。cookie
信息是可以通过后台代码、node
程序,post
到服务器上去的,用于通过验证。如果该项为勾选,说明该 cookie
信息只能通过本域名下的请求才能传输,不能通过其他域名下发送
cookie是有时间限制的,根据生命期不同分成两种:会话cookie和持久cookie;
如果不设置过期时间,则表示这个cookie生命周期为从创建到浏览器关闭止,只要关闭浏览器窗口,cookie就消失了。这种生命期为浏览会话期的cookie被称为会话cookie。会话cookie一般不保存在硬盘上而是保存在内存里。
如果设置了过期时间(setMaxAge(60_60_24)),浏览器就会把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 string
Value string
Path string
Domain string
Expires time.Time
RawExpires string
// 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 seconds
MaxAge int
Secure bool
HttpOnly bool
Raw string
Unparsed []string // Raw text of unparsed attribute-value pairs
}
我们来看一个例子,如何设置cookie
expiration := time.Now()
expiration = expiration.AddDate(1, 0, 0)
cookie := http.Cookie{Name: "username", Value: "lym", Expires: expiration}
http.SetCookie(w, &cookie)
Go读取cookie
上面的例子演示了如何设置cookie
数据,我们这里来演示一下如何读取cookie
cookie, _ := r.Cookie("username")
fmt.Fprint(w, cookie)
还有另外一种读取方式
for _, cookie := range r.Cookies() {
fmt.Fprint(w, cookie.Name)
}
可以看到通过request
获取cookie
非常方便。
3. session详解
session
,中文经常翻译为会话,其本来的含义是指有始有终的一系列动作/消息,比如打电话是从拿起电话拨号到挂断电话这中间的一系列过程可以称之为一个session
。然而当session
一词与网络协议相关联时,它又往往隐含了“面向连接”和/或“保持状态”这样两个含义。
session在Web开发环境下的语义又有了新的扩展,它的含义是指一类用来在客户端与服务器端之间保持状态的解决方案。有时候Session也用来指这种解决方案的存储结构。
session机制是一种服务器端的机制,服务器使用一种类似于散列表的结构(也可能就是使用散列表)来保存信息。
当程序需要为某个客户端的请求创建一个session
的时候,服务器首先检查这个客户端的请求里是否包含了一个session
标识-称为session id
,如果已经包含一个session id
则说明以前已经为此客户创建过session
,服务器就按照session id
把这个session
检索出来使用(如果检索不到,可能会新建一个,这种情况可能出现在服务端已经删除了该用户对应的session
对象,但用户人为的在请求的URL
后面附加上一个JSESSION
的参数)。如果客户请求不包含session id
,则为此客户创建一个session
并且同时生成一个与此session
相关联的session id
,这个session id
将在本次响应中返回给客户端保存。
三、小结
如上文所述,session
和cookie
的目的相同,都是为了克服http
协议无状态的缺陷,但完成的方法不同。session
通过cookie
,在客户端保存session id
,而将用户的其他会话消息保存在服务端的session
对象中,与此相对的,cookie
需要将所有信息都保存在客户端。因此cookie
存在着一定的安全隐患,例如本地cookie
中保存的用户名密码被破译,或cookie
被其他网站收集,例如:
appA
主动设置域B cookie
,让域B cookie
获取;XSS
,在appA
上通过javascript
获取document.cookie
,并传递给自己的appB
。
通过上面的一些简单介绍我们了解了cookie
和session
的一些基础知识,知道他们之间的联系和区别,做web
开发之前,有必要将一些必要知识了解清楚,才不会在用到时捉襟见肘,或是在调bug
时候如无头苍蝇乱转。
参考:
https://www.kancloud.cn/kancloud/web-application-with-golang/44177