基本认识
首先有一点必须特别的清楚: 因为HTTP协议是无状态的,客户每次读取web页面时,服务器都打开新的会话,而且服务器也不会自动维护客户的上下文信息,对于一个浏览器发出的多次请求,WEB服务器无法区分 是不是来源于同一个浏览器,更别说是否是来自同一用户。为了保持用户的状态,有了两种机制,一般用户客户端的cookie机制,和用于服务器端的session机制,这两种机制都是为了保持状态,既有联系又有区别。
cookie基本实现机制
现在的cookie是HTTP协议的一部分,一般存在HTTP的响应头,内容是一系列的键值对的形式,简单说:cookie就是服务器在用户的浏览器中存储的一小段文本文件(大小不能超过3K)不包含任何可执行代码,里面一般包含的是用户的登录信息之类的比较少的,用来验证用户是否合法(不止局限与此,cookie是用来记录状态的,也可以是购物的等一系列状态,让服务器知道我们浏览了那些地方,购物对那些感兴趣,一般很多广告都是根据这个东西来推送的,你在某购物网站买了一个东西,然后浏览别的网站,发现广告都是和你浏览的相关)。 cookie的内容主要包括:key-value,Expires(过期时间),path和domain。path和domain一起构成cookie的作用范围。
现在的cookie,内容经过加密了
cookie的实现流程:
- 浏览器向某个URL发起HTTP请求 (可以是任何请求,比如GET,POST等)
- 对应的服务器收到该HTTP请求,并做相应的响应(响应头和请求体两部分),在响应的头中加入
Set-Cookie
字段(设置相应的cookie,cookie是多个key-value
组成的) - 浏览器收到来自服务器的HTTP响应
- 浏览器在响应头中发现存在
Set-Cookie
字段,就会将相应的cookie(key-value)保存在内存或者硬盘中。需要注意的是Set-Value
字段可以包含多个cookie,每一项都可以指定过期时间,默认的过期的时间是用户浏览器关闭的时候 - 浏览器下次给该服务器发送HTTP请求时,会将服务器设置的cookie附加在HTTP请求头
Cookie
中浏览器可以存储多个不同域名下的Cookie,但只发送当前请求的域名曾经指定的域名,这个可以在Set-Cookie
中指定 - 服务器收到这个HTTP请求,发现请求头中有
Cookie
字段,就知道这个用户的状态。获取相应的信息进行响应。
这就是整个基本的cookie机制。保存了用户的操作状态,但是还需要注意的是,cookies是通过明文传递。在HTTP包中容易被劫持和伪造,是不安全的,不应该存一些比较重要的东西。还有就是cookie在整个会话都会在HTTP的请求中,增加了流量。
session的基本实现机制
有一个session是不能改变的,是为了维持住HTTP的状态,所以在用户每次发起HTTP请求的时候,都需要让服务器知道是那个用户发起的这个请求,然后查找这个用户的状态,在进行相应的处理。session的实现基于这点,就需要一个唯一的ID标志某个用户(session)然后在这个ID中对应多个键值对来保证用户的状态,所以前后端只需要传递一个sessionId,服务器就可找到对应的状态(这个对应的键值对可以存在redies或者数据库中)。前后端传递值基本有三种:一种是直接写在URL中,一种通过表单中的隐藏域来提交,还有一种是现在流行的做法,通过在cookie中设置一个键值对jsessionId=${sessionId}
在传递。现在第一二种都不是很建议这么做,当然在浏览器禁用的情况下也可以通过前面两种来传递,不过一般浏览器都支持使用cookie的方式。
两种方式的区别和联系
- cookie数据存放在客户的浏览器上,session数据放在服务器上。
- cookie不是很安全,别人可以分析存放在本地的cookie并进行cookie欺骗,考虑到安全应当使用session。
- session会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能
考虑到减轻服务器性能方面,应当使用cookie。 - 单个cookie保存的数据不能超过4K,很多浏览器都限制一个站点最多保存20个cookie。
联系是:两个都是用来保持HTTP协议状态的方式,一种是客户端的实现,一种是服务器端的实现。但是session可以依赖于cookie来传递sessionId
一个小项目例子–在微信中开发的小程序(公众号自动回复)里面的session问题
首先简单描述下小项目:用户在公众号中输入某一个触发关键词(项目里面的例子是:绑定),然后就进入绑定所涉及的流程。用户在公众中输入触发词,是由微信的服务器进行响应,然后转发到程序的服务器。程序的服务器在把处理的结果(用规定的格式)传递给微信的服务器,由微信的服务器进行响应给用户。整个项目用golang开发,简单的使用了beego框架。
基于session来实现(初始版)
1 | func (c *MainController) Dispatch() { |
现在这个程序是执行不了的。在beego中默认传递sessionId是cookie。但如果我在程序中必须使用session保持用户的状态(不然流程没办法继续下去)。两个服务器之间的交互式没有办法传递cookie。所以就会出现在一直在第一个流程,无法进入第二个流程。
解决
首先明白一点,sessionId的作用是一个唯一标识符,用来标记同一个用户。但是在微信的整个架构中有一个跟sessionId很类似的东西:openid:用户对于某个公众号唯一的标识。所以在微信服务器向程序的服务器提交POST消息市本身也会自带这个openid。这样就为解决session提供了遍历。
不需要额外去产生和传递sessionId。直接使用openid来作为用户的唯一标识符。
然后对于sessionId对应的具体内容我选择了方便的redis来存储
实现代码–直接复写了beego的Getseeion和SetSession方法
1 | func (c *MainController)initSession(sid string) { |
完整代码(包括微信golang的接入,大鱼短信,beego,session-redis的具体实现):
总结
session中的sessionId是用来标志唯一用户的。通过找到这个用户来判断用户的状态