关于http的无状态
我们都知道http
是一个无状态的协议,其无状态的特性主要体现在:从服务器的角度来看,服务器并不知道自己接收到的多条请求之间是否存在相互联系,因为在http
协议中并没有做这样的规定。然后,这样的需求是很普遍的,比如说:有一条接口为/vip
还有一条接口为/login
。我们的业务需要在/login
接口中验证用户名和密码正确后,才可以访问/vip
接口。因为刚才提到的无状态特性,导致我们首先不可以把/login
接口和/vip
接口建立起一个必然的联系。因此,我们需要一种方式能够建立起两条接口之间的联系。
如何让http存在状态
其实这个问题也很简单,基本思路就是在/login
接口成功之后,服务端返回一个签名,然后/vip
接口发起请求后如果能携带这个签名的话,那么我们就能知道/vip
和之前的/login
是有关系的。也可以根据签名的真伪去决定是否可以访问/vip
接口。
现在生成签名基本上是两种方案分别是
session
和token
使用session
的方案
使用session
的方案,就是当/login
请求成功后,服务端生成一个签名,这个签名有三个特性
- 签名本身没有任何意义
- 签名应该是唯一的
- 服务端必须存储该签名(redis,内存,文件系统等等),方式任选。
服务器生成签名之后,一定要返回给浏览器端,浏览器端也需要对该签名做存储(可以选在存储在cookie,webStorage等),之后服务器再发起请求的时候需要携带该签名,然后服务端就可以根据该签名去建立两条接口之间的联系了。
通过流程图来看一下这种方式
使用session的方式会存在一些其他的问题,比如服务端存储的签名消失了,那么状态也就不存在了!思考一下一般在服务端哪些行为会让sessioin数据消失
使用token
的方案
使用token方案的原理,同使用session
方案的原理基本差不多!不同的地方就是服务端生成的签名不再是无意义的了,而是把一些有用的信息加密后得到的一份签名,也就是说该签名是存储有信息的。同时,该签名也不需要存储在服务端了。当然,该签名只能由特定的服务端进行解密。流程图如下:
JWT
的规范
JWT
是一个轻巧的规范,意思是json web token
。这个规范规定了如何生成token
;
一个JWT
实际包含三部分:
- 头部
- 载荷
- 签名
载荷
载荷部分呢,其实就是一个JSON对象。可以在这里添加很多信息(希望在token中传递的信息都可以添加),除此之外呢,还有一些特定的字段
{
"iss": "John Wu JWT",
"iat": 1441593502,
"exp": 1441594722,
"aud": "www.example.com",
"sub": "jrocket@example.com",
"from_user": "B",
"target_user": "A"
}
如下是特定的字段:
- iss: 该JWT的签发者
- sub: 该JWT所面向的用户
- aud: 接收该JWT的一方
- exp(expires): 什么时候过期,这里是一个Unix时间戳
- iat(issued at): 在什么时候签发的
把上边提到的JSON对象进行base64
编码,得到一串字符串,就是载荷,陈之为Payload
头部(header)
头部用于描述JWT
最基本的信息,例如其类型以及签名所用的算法。
{
"typ": "JWT",
"alg": "HS256"
}
这里表名我们所用的算法是HS256算法
对该JSON对象也使用base64
编码,得到的字符串就是头部(Header)。
签名
把Payload
和Header
用.
连接起来(Header在前)。得到一个新的字符串
最后,我们将上面拼接完的字符串用HS256
算法进行加密。在加密的时候,我们还需要提供一个密钥(secret)。那么就可以得到我们加密后的内容,这就是签名。
完整的JWT
最后,我们把做如下拼接
`${Header}.${Payload}.${签名}`
这就是完整的JWT
。
node-jsonwebtoken
node-jsonwebtoken
这个包已经完全实现了上述的JWT
规范,使用该包就可以生成token
字符串了
安装
npm install npm install jsonwebtoken
生成token
var jwt = require('jsonwebtoken');
var token = jwt.sign({ foo: 'bar' }, 'shhhhh');
解析token
var jwt = require('jsonwebtoken');
var decoded = jwt.verify(token, 'shhhhh');