Bootstrap

Cookie,Session,Token和JWT的基本使用以及区别介绍

1. 前言

由于http协议的无状态性。每一次的http请求不会记住和保存任何会话信息,比如上一次的请求发送者和这一次的发送者是不是同一个人?每次请求都是全新和独立的。随着交互式Web应用的兴起, 像在线购物网站,需要登录的网站等,马上面临一个问题,就是要管理回话,记住那些人登录过系统,哪些人往自己的购物车中放商品,也就是说我必须把每个人区分开。

2. Cookie

Cookie是浏览器实现的一种数据存储技术。一般由服务器生成,发送给浏览器(客户端也可进行cookie设置)进行存储,下一次请求同一网站时会把该cookie发送给服务器。

cookie

2.1 Cookie的特点

1:cookie存储在客户端(浏览器),发送请求时自动携带放在请求头中。

2:cookie只能以文本的方式保存字符串类型的数据。

3:单个cookie保存的数据不能超过4KB。

4:cookie的安全性不高,别人可以分析存放在本地的cookie并进行cookie欺骗。

5:cookie默认不可跨域,可通过特殊的操作如设置withCredentials属性为true实现跨域。(参考链接:跨域的介绍)。

2.2 Cookie的属性

cookie的每个属性都通过name=value的形式进行设置,属性之间通过分号加空格进行分割。我们可以根据需求去自定义cookie中需要传递的数据,以下是cookied的一些内置原生的可设置的基本属性:

属性名称说明
domain指定cookie所属域名,默认当前域名
path指定cookie在哪个域名路由下生效,默认为/
maxAge指定cookie的有效时长,单位为秒。当值小于0时表示当前会话关闭后即失效,为-1时表示删除该cookie。默认为-1。
expires指定cookie的有效截至日期(现已被maxAge取代)。
secure指定cookie是否仅被使用安全协议传输。当该值为true,则cookie仅在https中生效,http无效。默认为false
httpOnly若该属性有值,则客户端无法通过JS脚本去获取cookie信息,但仍可以在浏览器的Application工具中手动修改cookie。具备一定程度的安全性。

2.3 Cookie的设置

1:客户端设置

可通过JavaScript脚本来获取和设置cookie。客户端可以设置expires, domain, path, secure(只有在https协议的网页中, 客户端设置secure类型cookie才能生效), 但无法设置httpOnly选项。

示例:

document.cookie = "name=xiaoming; age=12 "

2:服务端设置

浏览器会发送HTTP请求到服务端后,服务器在进行响应到客户端时可以在响应头中设置Set-Cookie属性从而保存cookie在客户端中。注意:

  • 一个Set-Cookie属性只能设置一个cookie, 当你想设置多个, 需要添加同样多的Set-Cookie
  • 服务端可以设置cookie的所有选项: expires, domain, path, secure, HttpOnly。

2.4 在iframe中cookie无效的解决方案

参考文章:https://juejin.cn/post/6935683384710529055

3. Session

session 是另一种记录服务器和客户端会话状态的机制,并且session 是基于 cookie 实现的。服务器要知道当前请求发给自己的是谁,为了做这种区分,服务器就是要给每个客户端分配不同的"身份标识",然后客户端每次向服务器发请求的时候,都带上这个“身份标识”。

session

session 认证流程:

用户第一次请求服务器的时候,服务器根据用户提交的相关信息,创建对应的 Session。请求返回时将此 Session 的唯一标识信息 SessionID 返回给浏览器。浏览器接收到服务器返回的 SessionID 信息后,会将此信息存入到 Cookie 中,同时 Cookie 记录此SessionID 属于哪个域名。

当用户第二次访问服务器的时候,请求会自动判断此域名下是否存在 Cookie 信息,如果存在自动将 Cookie 信息也发送给服务端,服务端会从 Cookie 中获取 SessionID,再根据 SessionID 查找对应的 Session 信息,如果没有找到说明用户没有登录或者登录失效,如果找到 Session 证明用户已经登录可执行后面操作。

3.1 Session的特点

1:session 是基于 cookie 实现,cookie失效或删除则session也无法获取。

2:Session 是存储在服务器端,所以安全性比cookie高。

3:Session 可以存任意数据类型。

4: Session的默认生效时间是30分钟。只要在生效时间内,即使该session值已被修改,依然可通过旧有Cookie访问到旧有Session值。

5:Session 可存储数据的容量远高于 Cookie,但是当访问量过多,会占用过多的服务器资源。

4. Token

虽然Session具备一定的安全性,但是它的问题在于扩展性不好。比如涉及到服务器集群的场景,要求每台服务器都能够读取session。这种场景一般解决方案是session共享,经典应用需求是A和B两个关联网站间的单点登录,但是这种方案一般工程量较大。于是便出现了另一种服务器无需保存session的技术方案:Token

token

验证流程

1:客户端使用用户名跟密码请求登录。

2:服务端收到请求,去验证用户名与密码。

3:验证成功后,服务端会签发一个 token 并把这个 token 发送给客户端。

4:客户端收到 token 以后,会把它存储起来,比如放在 cookie 里或者 localStorage 里。

5:客户端每次向服务端请求资源的时候需要带着服务端签发的 token。

6:服务端收到请求,然后去验证客户端请求里面带着的 token ,如果验证成功,就向客户端返回请求的数据。

4.1 Token的特点

1:每一次请求都需要携带 token,需要把 token 放到 HTTP 的 Header 里。

2:token签发后存储在客户端,不占用服务器资源,可减轻服务器压力。

3:使用token无需担心跨域问题,可自由使用。

5. JWT

JWT(Json Web Token):Token技术的一种现有标准,也是目前最流行的跨域认证解决方案。JWT主要由Header(头部),Payload(负载),Signature(签名)三部分组成。如下图示例:

JWT

jwt流程

JWT与Token的认证过程基本相似,主要的不同点在于:

Token:服务端验证客户端发送过来的 Token 时,还需要查询数据库获取用户信息,然后验证 Token 是否有效。

JWT:将 Token 和 Payload 加密后存储于客户端,服务端只需要使用密钥解密进行校验(校验也是 JWT 自己实现的)即可,不需要查询或者减少查询数据库,因为 JWT 自包含了用户信息和加密的数据。

5.1 JWT的使用方式

jwt一般可暂时存储在localstorage或cookie中
方便获取

1: 将JWT放在Cookie 里面自动发送,缺点是默认不可跨域。

2: 将JWT放在HTTP 请求头信息的 Authorization 字段里(主流做法)。

如下所示:

Authorization: Bearer <token>

3:JWT就放在POST请求的数据体里。

4:JWT放在GET请求的参数里(即URL里)

6. 注意事项

1:移动端对 cookie 的支持不是很好,而 session 需要基于 cookie 实现,所以移动端常用的是 token

2:cookie由于安全性不高,应避免存储一些比较重要的敏感数据。

3:使用session时,假如浏览器禁止 cookie 或不支持 cookie , 一般会把 sessionId 跟在 url 参数后面即重写 url,所以 session 不一定非得需要靠 cookie 实现。

7. 单点登录实现

多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。

A 网站和 B 网站是同一家公司的关联服务。现在要求,用户只要在其中一个网站登录,再访问另一个网站就会自动登录,请问怎么实现?

这种需求需要解决跨域认证 以及 前端页面 JavaScript 跨域 问题

7.1 跨域认证解决

7.1.1 session持久化

如果采用传统的session用户验证机制,则需要session实现多系统共享。那么标准的实现方案就是​ 将 session 数据持久化,写入数据库或别的持久层。各种服务受到请求后,都向持久层请求数据。

具体实现流程参考:

1:用户在A王网站登录成功后,将 session 存储到 Redis 持久化存储,注意设置有效期。

2:此时访问 网站B 进行用户身份认证。注意 此时 访问 B的链接URL 要携带参数 sessionid!B 在处理请求- 身份验证时,先解析是否携带了sessionid参数,携带了则向 redis 中查询相关数据,并将数据保存到当前会话中。此时就成功 登录 B 了。

7.1.2 JWT方案

用户在A网站登录成功后,服务器在客户生成加密的Token(如包含下面的信息),然后此时访问B系统同样携带此Token。服务端统一通过算法进行解密验证即可。

{
 "姓名": "张三",
 "角色": "管理员",
 "到期时间": "2019年10月1日0点0分"
}

7.2 JS跨域解决

单点登录难免会遇到窗口之间 JS 跨域问题,此时的解决方案是 postMessage

postMessage 是HTML5 XMLHttpRequest Level 2中的API,且是为数不多可以跨域操作的window属性之一,它可用于解决以下方面的问题:

a.) 页面和其打开的新窗口的数据传递

b.) 多窗口之间消息传递

c.) 页面与嵌套的iframe消息传递

d.) 上面三个场景的跨域数据传递

用法:postMessage(data,origin) 方法接受两个参数

data: html5规范支持任意基本类型或可复制的对象,但部分浏览器只支持字符串,所以传参时最好用JSON.stringify()序列化。

origin: 协议+主机+端口号,也可以设置为"*“,表示可以传递给任意窗口,如果要指定和当前窗口同源的话设置为”/"。

1.)a.html:(http://www.domain1.com/a.html)

<iframe id="iframe" src="http://www.domain2.com/b.html" style="display:none;"></iframe>
<script> 
 var iframe = document.getElementById("iframe");
 iframe.onload = function() {
 var data = {
 name: "aym"
 };
 // 向domain2传送跨域数据
 iframe.contentWindow.postMessage(JSON.stringify(data), "http://www.domain2.com");
 };
 // 接受domain2返回数据
 window.addEventListener("message", function(e) {
 alert("data from domain2 ---> " + e.data);
 }, false);
</script>

2.)b.html:(http://www.domain2.com/b.html)

<script>
 // 接收domain1的数据
 window.addEventListener("message", function(e) {
 alert("data from domain1 ---> " + e.data);
 var data = JSON.parse(e.data);
 if (data) {
 data.number = 16;
 // 处理后再发回domain1
 window.parent.postMessage(JSON.stringify(data), "http://www.domain1.com");
 }
 }, false);
</script>
;