为何会有cookie
session
token
及jwt
呢?简单点说就是为了标记HTTP
的 状态。
HTTP
为何需要标记状态呢?因为http
是一个没有状态的协议。一切的根源都在于此!
继续追问,什么叫没有状态
呢?就是指同一个客户端前一次HTTP
请求与这次的HTTP
请求,相关之间不认识,没法确定前一次请求与这次请求是否属于同一个客户。这也就意味着,如果你上一次请求刚成功登录系统,但这次请求时系统已经忘记你是谁了。系统不知道每次请求来自于那个客户,只能反馈同样的内容。
因为无状态,所以需要借助 cookie
session
token
及jwt
将多次请求关联起来。
HTTP协议
这里先简单介绍下HTTP协议。
HTTP 是基于 TCP/IP 协议的应用层协议
在Web应用中,服务器把网页传给浏览器,实际上就是把网页的HTML代码发送给浏览器,让浏览器显示出来。而浏览器和服务器之间的传输协议是HTTP。
HTTP是在网络上传输HTML的协议,用于浏览器和服务器的通信。
在正式介绍cookie
session
token
及jwt
前,先了解两个概念:认证
、授权
。
认证
认证(Authentication)简单来讲就是验证当前请求者的身份
比如常用的 用户名和密码、手机号和验证码、电子邮箱和密码等,还有一些新技术,指纹、语音、虹膜 等生物学认证手段。
授权
授权(Authorization)简单来讲就是授予第三方访问用户资源的权限
常用一下三种方式完成授权工作:
- cookie 机制,一个网站的cookie保持这用户的授权信息
- session 机制,一个访问会话保持着用户的授权信息
- token 机制,主要是通过给客户端颁发授权令牌(token)来实现,一个合法有效的令牌中保持着用户的授权信息
有人要问了jwt
哪儿去了,看这里JSON Web Token
,是不是若有所思啊,就是简称缩写,可以理解为另一种token,是目前最流行的跨域认证解决方案。
cookie
cookie 是服务器发送到用户浏览器并保存在本地的一小块数据,它会在浏览器下次向同一服务器再发起请求时被携带并发送到服务器上,是
储存在用户本地终端上的数据
。
cookie为了弥补HTTP在状态管理上的不足而设计的。
cookie与域名(子域名)具有唯一关联性,所以是不可以跨域的。
服务器向客户端发送 Cookie 是通过 HTTP 响应报文实现的,在 Set-Cookie 中设置需要向客户端发送的cookie,cookie格式如下:
Set-Cookie: value[; expires=date][; domain=domain][; path=path][; secure]
cookie的简单工作流程
- 浏览器向服务器发送请求
- 服务器接到请求并响应请求,向浏览器设置cookie
- 浏览器接到响应数据,把cookie存储在本地,下次请求是带上cookie
- 服务器接到请求,识别cookie
cookie常见属性
属性名 | 解释 |
---|---|
name=value | 键值对,设置 Cookie 的名称及相对应的值 |
domain | 指定 cookie 所属域名,默认是当前域名。如果 cookie 的 domain 设置为 taobao.com,那么 item.taobao.com, order.taobao.com 都是可以共享 cookie 的, 但是访问 tmall.com 就不能共享 cookie 了,这就涉及跨域访问的问题,跨域问题如何解决 |
path | 指定 cookie 在哪个路径(路由)下生效,默认是 '/'。如果设置为 /abc,则只有 /abc 下的路由可以访问到该 cookie |
expires | 指定 cookie 的过期时间(GMT时间格式),到达该时间点后该 cookie 就会自动失效 |
max-age | cookie 有效期,单位秒。如果为正数,则该 cookie 在 max-age 秒后失效;如果为负数,该 cookie 为临时 cookie ,关闭浏览器即失效,浏览器也不会以任何形式保存该 cookie ;如果为 0,表示删除该 cookie 。默认为 -1。HTTP 1.1中定义的,优先级高于 expires 字段 |
HttpOnly | 如果给某个 cookie 设置了 httpOnly 属性,则无法通过 JS 脚本读写该 cookie 的信息 |
secure | 该 cookie 是否仅被使用安全协议传输,默认为false。当 secure 值为 true 时,cookie 在 HTTP 中是无效的。 |
session
session 翻译过来就是『会话』。用户打开一个浏览器, 点击多个超链接, 访问服务器多个web资源, 然后关闭浏览器, 整个过程称之为一个会话。
session 是另一种记录服务器和客户端会话状态的机制。
session 存储在服务器端,一般是文件中,也可以存在数据库或缓存中。
session 一般与 cookie 搭配使用。session 中包含敏感信息存储在服务器端,通常将 sessionId
存储在客户端的 cookie
中,客户端每次请求携带 sessionId
即可识别用户。如果客户端禁用了cookie,则可以通过改写URL携带sessionId、提交sessionId参数等方法传递sessionId
。
session 简单工作流程
- 用户第一次请求时,提交用户名密码等信息进行登录认证,服务器根据用户提交的信息进行鉴权,鉴权成功后创建 session 对象,并将
sessionId
塞入cookie
中返回给浏览器客户端,浏览器收到响应信息将cookie
存入本地。 - 用户第二次请求时,浏览器自动将当前域名下的
cookie
信息发送给服务端,服务端解析cookie
,获取到sessionId
后再查找对应的session
对象,如果session
对象存在说明用户已经登录,继续下一步操作。
token
Token是验证用户身份的凭证。Token是服务端生成的一串字符串,以作客户端进行请求的一个令牌,当第一次登录后,服务器生成一个Token便将此Token返回给客户端,客户端把Token存储在本地,可以是cookie也可以是Local Storage,以后客户端只需带上这个Token前来请求数据即可,无需再次带上用户名和密码,服务器端接收到Token 进行验证,如果正确按需返回请求数据。
token 的组成
- 用户唯一的身份标识(uid、account等)
- 当前时间戳(time())
- 签名(sign 签名,token 的前几位以哈希算法压缩成的一定长度的十六进制字符串)
JWT
目前最流行的跨域认证解决方案。
是为了在网络应用环境间传递声明而执行的一种基于 JSON 的开放标准(RFC 7519)
JWT 的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源。
JWT 由三部分组成
- header,头部
Header 部分是一个 JSON 对象,描述 JWT 的元数据
{
"alg": "HS256", // 签名的算法(algorithm),默认是 HMAC SHA256(写成 HS256)
"typ": "JWT" // 这个令牌(token)的类型(type)
}
header部分就是把上面的json对象使用算法Base64URL
转为字符串。
- payload,载物、负载
Payload 部分也是一个 JSON 对象,用来存放实际需要传递的数据。JWT 规定了7个官方字段,供选用,也可以自定义字段。这部分数据默认是不加密的,私密重要信息不可以明文放在这里。
{
"iss": "签发人(issuer)",
"exp": "过期时间(expiration time)",
"sub": "主题(subject)",
"aud": "受众(audience)",
"nbf": "生效时间(Not Before)",
"iat": "签发时间(Issued At)",
"name": "自定义字段",
"age": 自定义字段
}
payload部分就是把上面的json对象使用算法Base64URL
转为字符串。
- signature,签名
signature 对前两部分的签名,防止数据被篡改。
这里需要指定一个密钥(secret)。这个密钥只有服务器才知道,不能泄露给用户。然后,使用 Header 里面指定的签名算法(默认是 HMAC SHA256),按照下面的公式生成签名。
HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload),secret)
JWT就是一串用点(·)把header
payload
signature
三部分字符串连接起来的长字符串。
JWT = Header.Payload.Signature
最后把这个长字符串返回给用户。客户端收到服务器返回的 JWT,可以储存在 Cookie 里面,也可以储存在 localStorage。此后,客户端每次与服务器通信,都要带上这个 JWT。比如放在 HTTP 请求的头信息Authorization字段里面。
Authorization: Bearer <token>
亦或者放在post请求的请求体数据中。
JWT特点
- 为了减少盗用,JWT 不应该使用 HTTP 协议明码传输,要使用 HTTPS 协议传输
- 保护好 secret 私钥,该私钥非常重要
- payload部分不能放置私密信息
总结
在分布式微服务技术日趋流行的今天,大型网站的设计都尽量避免使用 session 实现 HTTP 状态化。单机提供服务当然没有问题,如果是服务器集群的话,或者是跨域的服务导向架构,就要求 session 数据共享,每台服务器都能够读取 session,这就需要对 session 数据进行持久化存储,对空间的要求会变大,同时工程量也比较大。
服务器不保存任何 session 数据,也就是说,服务器变成无状态了,从而比较容易实现扩展。