傻傻分不清之Cookie、Session、Token、JWT

傻傻分不清之Cookie、Session、Token、JWT

1. 基础概念

什么是认证(Authentication)

通俗的讲就是验证当前用户的身份,证明“你是你自己”(比如:你每天上下班打卡,都需要通过指纹打卡,当你的指纹和系统里录入的指纹相匹配时,就打卡成功)

互联网中的认证:

  • 用户名账号、密码登录
  • 邮箱发送登录链接
  • 手机号接收验证码
  • 只要你能收到邮箱验证码,就默认你是账号的主人

什么是授权(Authorization)

  • 用户授予第三方应用访问该用户某些资源的权限
  • 你在安装手机应用的时候,App会询问是否允许授予权限(访问相册、地理位置等权限)
  • 你在访问微信小程序时,当登录时,小程序会询问是否允许授予权限(获取昵称、头像、地区、性别等个人信息)
  • 实现授权的方式有:cookie、session、token、OAuth

什么是凭证(Credentials)

  • 实现认证和授权的前提是需要一种媒介(证书)来标记访问者的身份
  • 在战国时期,商鞅变法,发明了照身帖。照身帖由官府发放,是一块打磨光滑细密的竹板,上面刻有持有人的头像和籍贯信息。国人必须持有,如果没有就被认为是黑户,间谍之类的。
  • 在现实生活中,身份证就是我们认证的凭证
  • 在互联网应用中,一般网站(如掘金)会有两种模式,游客模式和登录模式。游客模式下,可以正常浏览网站上面的文章,一旦想要点赞/收藏/分享文章,就需要登录或者注册账号。当用户的登录成功后,服务器就会给该用户使用的浏览器颁发一个令牌(token),这个令牌用来表名你的身份,每次浏览器发送请求时会带上这个令牌,就可以使用游客模式下无法使用的功能。

2. Cookie

  • HTTP 是无状态的协议,客户端向服务器发请求,服务器返回响应,每次请求是独立的,但是下次发请求如何让服务端知道客户端是谁呢?这种背景下,就产生了Cookie。
  • Cookie存储在客户端:cookie是服务器发送到用户浏览器并保存在本地的一小块数据,它会在浏览器下次向同一服务器再发起请求时被携带并发送到服务器上。因此,服务端脚本就可以读、写存储在客户端的cookie的值。
  • Cookie是不可跨域的:每个cookie都会绑定单一的域名(绑定域名下的子域都是有效的),无法在别的于明霞获取使用,同域名不同端口也是允许共享使用的。

服务端只需要设置setCookie这个header,之后浏览器会自动把cookie写入到我们的浏览器存起来,然后当前域名在发送请求的时候都会自动带上这个cookie。

Set-Cookie: "name=value;domain=.domain.com;path=/;expires=Sat, 11 Jun 2016 11:29:42 GMT;HttpOnly;secure"
2.1 Cookie 属性说明
属性说明
name=value键值对,设置Cookie的名称及相对应的值,都必须是字符串类型(name不区分大小写) — 如果值为Unicode字符,需要为字符编码。 — 如果值为二进制数据,则需要使用哦BASE64编码。
domain指定cookie所属域名,默认是当前域名
path指定cookie在那个路径(路由)下生效,默认是‘/’ 如果设置为 /abc,则只有在 /abc下的路由可以访问到该cookie,如:/abc/read。
expires过期时间(GMT时间格式),在设置的某个时间点后该cookie就会失效。 如果客户端和服务器时间不一致,使用expires就会存在偏差。 一般浏览器的cookie都是默认储存的,当关闭浏览器结束这绘画的时候,这个cookie也就会被删除
max-agecookie有效期,单位秒。如果为正数,则该cookie在maxAge秒后失效。如果为负数,该cookie为临时cookie,关闭浏览器即失效,浏览器也不会以任何形式保存该cookie。如果为0,表示删除cookie。默认为-1。 — 优先级高于expires
HttpOnly如果给某个cookie设置了httpOnly属性,则无法通过JS脚本读写该cookie的信息,但还是能通过Application中手动修改cookie,所以只是在一定程度上可以防止CSRF攻击,不是绝对的安全
secure该cookie是否仅被使用安全协议传输。安全协议有HTTPS,SSL等,在网络上传输数据之前先将数据加密,默认为false。 当secure的值为true是,cookie在HTTP中是无效的。

Cookie集合中的每个cookie都拥有这些属性,而且每个cookie的这些属性都是独立分开的,各自控制各自的cookie。

2.2 Cookie的特点
  1. cookie可能被禁用
  2. cookie是与浏览器相关的。不同浏览器所保存的cookie也是不能互相访问的
  3. cookie可能被用户删除
  4. cookie安全性不够高。如果要保存用户名密码等信息时,最好事先经过加密处理。
  5. 存储的数据量为4kb大小,cookie只支持存储string类型的数据
  6. 简单易用
  7. 信息存储于用户硬盘,因此可以作为全局变量
每个域名下的cookie个数有限制
  • Chrome和Safari没有做硬性限制
  • Firefox最多50个cookie
  • IE7和之后的版本最后可以有50个cookie
  • IE6或更低版本最多20个cookie
2.3 客户端对Cookie的存取
  • 读取cookie

    可以用document.cookie获取当前页面可用的cookie集合,其返回的值是一个字符串,该字符串都是由一系列键/值对组成,不同键/值对之间是通过“分号和空格隔开。”

    例如:

    document.cookie;
    // "name1=value1; name2=value2"
    

    这些返回的cookie的值并不包含键/值以外的其他cookie属性。

  • 设置cookie:

      document.cookie = `name=${encodeURIComponent(name)}; max-age=1000;`;
    

    name这个cookie会被添加到现有的cookie集合中。

    由于cookie的键/值中的值是不允许包含分号、逗号和空白符,因此,在存储前一般可以采用 encodeURIComponent() 函数对值进行编码。相应的,读取cookie值的时候要用 decodeURIComponent() 函数解码。

  • 更新cookie

    要改变cookie的值,需要使用相同的名字、路径和域,但是新的值会重新设置cookie的值。同样地设置新 max-age 属性就可以改变原来的cookie的有效期。

  • 删除cookie

​ 要删除一个cookie,需要使用相同的名字、路径和域,然后制定一个任意(非空)的值,并且将max-age属性指定为0,在此设置cookie

2.4 服务器端设置cookie示例(Node)
var http = require('http');
var fs = require('fs');
 
http.createServer(function(req, res) {
    res.setHeader('status', '200 OK');
    res.setHeader('Set-Cookie', 'isVisit=true;domain=.yourdomain.com;path=/;max-age=1000');
    res.write('Hello World');
    res.end();
}).listen(8888);
 
console.log('running localhost:8888')

3. Session

  • session是另外一种记录服务器和客户端会话状态的机制
  • session是居于cookie实现的,session存储在服务器端,sessionld会被存储到客户端的cookie中

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-a7TZL5NM-1664528858252)(C:\Users\wb.guohaofei01\Desktop\1.png)]

session 认证流程:

  • 用户第一次请求服务器的时候,服务器根据用户提交的相关信息,创建对应的 Session

  • 请求返回时将此 Session 的唯一标识 SessionID 返回给浏览器

  • 浏览器接收到服务器返回的 SessionID 后,会将此信息存入到 Cookie 中,同时 Cookie 记录此 SessionID 属于哪个域名

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

根据以上流程可知,SessionID 是连接 Cookie 和 Session 的一道桥梁,大部分系统也是根据此原理来验证用户登录状态。

4. Cookie和Session的区别

  • 安全性:Session比Cookie安全,Session是存储在服务器端的,Cookie是存储在客户端的
  • 存取值的类型不同:Cookie值支持存字符串数据,Session可以存任意数据类型
  • 有效期不同:Cookie可以设置为长时间保持,比如我们经常使用默认登录功能,Session一般失效时间较短,客户端关闭(默认情况下)或者Session超时都会失效
  • 存储大小不同:单个Cookie保存的数据不能超过4k,Session可以存储数据大小远高于Cookie,但是当访问量过多,会占用过多的服务器资源。

5. Token

Token是访问接口(API)时所需要的资源凭证

简单token组成:

uid(用户唯一的身份标识)、time(当前事件的时间戳)、sign(签名,token的前几位以哈希算法压缩成的一定长度的十六进制字符串)

特点:

  • 服务端无状态化、可扩展性好
  • 支持移动端设备
  • 安全
  • token完全由应用管理,所以它可以避开同源策略
Access Token

Access Token 的身份验证流程:

在这里插入图片描述

  1. 客户端使用用户名跟密码请求登录
  2. 服务端收到请求,去验证用户名与密码
  3. 验证成功后,服务端会签发一个token并把这个token发送给客户端
  4. 客户端收到token以后,会把它存储起来,比如放在localStorage里
  5. 客户端每次发送请求的时候需要把token放到请求的Header里传给服务端
  6. 服务端收到请求,然后去验证客户端请求里面带着的token,如果验证成功,就想客户端返回请求的数据

Refresh Token

  • 另外一种token ——refresh token
  • refresh token 是专用于刷新access token的token。如果没有refresh token,也可以刷新access token,但每次刷新都要用户输入登录用户名与密码,会很麻烦。有了refresh token,可以减少这个麻烦,客户端直接用refresh token去更新access token,无需用户进行额外的操作

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3HIZwPMp-1664528858254)(C:\Users\wb.guohaofei01\Desktop\3.png)]

  • Access Token 的有效期比较短,当 Acesss Token 由于过期而失效时,使用 Refresh Token 就可以获取到新的 Token,如果 Refresh Token 也失效了,用户就只能重新登录了

  • Refresh Token 的过期时间是存储在服务器的数据库中,只有在申请新的 Acesss Token 时才会验证,不会对业务接口响应时间造成影响,也不需要向 Session 一样一直保持在内存中以应对大量的请求

6. Token 和 Session 的区别

  • Session 是一种记录服务器和客户端会话状态的机制,使服务端有状态化,可以记录会话信息。而 Token 是令牌,访问资源接口(API)时所需要的资源凭证。Token 使服务端无状态化,不会存储会话信息。
  • Session 和 Token 并不矛盾,作为身份认证 Token 安全性比 Session 好,因为每一个请求都有签名还能防止监听以及重复攻击,而 Session 就必须依赖链路层来保障通讯安全了。如果你需要实现有状态的会话,仍然可以增加 Session 来在服务器端保存一些状态
  • 如果你的用户数据可能需要和第三方共享,或者允许第三方调用 API 接口,用 Token 。如果永远只是自己的网站,自己的 App,用什么就无所谓了

7. JWT

  • JSON Web Token(简称 JWT)是目前最流行的跨域认证解决方案。
    是一种认证授权机制
  • JWT 是为了在网络应用环境间传递声明而执行的一种基于 JSON 的开放标准。JWT 的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源。比如用在用户登录上
  • 可以使用 HMAC 算法或者是 RSA 的公/私秘钥对 JWT 进行签名。因为数字签名的存在,这些传递的信息是可信的
7.1 JWT 的原理

JWT 的原理是,服务器认证以后,生成一个 JSON 对象,返回给用户,就像下面这样

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

以后,用户与服务端通信的时候,都要发回这个 JSON 对象。服务器完全只靠这个对象认定用户身份。为了防止用户篡改数据,服务器在生成这个对象的时候,会加上签名。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VfsxDrHa-1664528858255)(C:\Users\wb.guohaofei01\Desktop\4.png)]

JWT认证流程

  • 用户输入用户名/密码登录,服务端认证成功后,会返回给客户端一个 JWT

  • 客户端将 token 保存到本地(通常使用 localstorage,也可以使用 cookie)

  • 当用户希望访问一个受保护的路由或者资源的时候,需要请求头的 Authorization 字段中使用Bearer 模式添加 JWT,其内容看起来是下面这样

Authorization: Bearer <token>
  • 服务端的保护路由将会检查请求头 Authorization 中的 JWT 信息,如果合法,则允许用户的行为

  • 因为 JWT 是自包含的(内部包含了一些会话信息),因此减少了需要查询数据库的需要

  • 因为 JWT 并不使用 Cookie 的,所以你可以使用任何域名提供你的 API 服务而不需要担心跨域问题

  • 因为用户的状态不再存储在服务端的内存中,所以这是一种无状态的认证机制

8. Token 和 JWT 的区别

相同:

  • 都是访问资源的令牌

  • 都可以记录用户的信息

  • 都是使服务端无状态化

  • 都是只有验证成功后,客户端才能访问服务端上受保护的资源

区别:

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

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

9. 要注意的问题

1. 使用 session 时需要考虑的问题
  • 将 session 存储在服务器里面,当用户同时在线量比较多时,这些 session 会占据较多的内存,需要在服务端定期的去清理过期的 session
  • 当网站采用集群部署的时候,会遇到多台 web 服务器之间如何做 session 共享的问题。因为 session 是由单个服务器创建的,但是处理用户请求的服务器不一定是那个创建 session 的服务器,那么该服务器就无法拿到之前已经放入到 session 中的登录凭证之类的信息了。
  • 当多个应用要共享 session 时,除了以上问题,还会遇到跨域问题,因为不同的应用可能部署的主机不一样,需要在各个应用做好 cookie 跨域的处理。

  • sessionId 是存储在 cookie 中的,假如浏览器禁止 cookie 或不支持 cookie 怎么办? 一般会把 sessionId 跟在 url 参数后面即重写 url,所以 session 不一定非得需要靠 cookie 实现

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

2. 使用 JWT 时需要考虑的问题
  • 因为 JWT 并不依赖 Cookie 的,所以你可以使用任何域名提供你的 API 服务而不需要担心跨域问题

  • JWT 默认是不加密,但也是可以加密的。生成原始 Token 以后,可以用密钥再加密一次

  • JWT 不加密的情况下,不能将秘密数据写入 JWT

  • JWT 不仅可以用于认证,也可以用于交换信息。有效使用 JWT,可以降低服务器查询数据库的次数

  • JWT 最大的优势是服务器不再需要存储 Session,使得服务器认证鉴权业务可以方便扩展。但这也是 JWT 最大的缺点:由于服务器不需要存储 Session 状态,因此使用过程中无法废弃某个 Token 或者更改 Token 的权限。也就是说一旦 JWT 签发了,到期之前就会始终有效,除非服务器部署额外的逻辑

  • JWT 本身包含了认证信息,一旦泄露,任何人都可以获得该令牌的所有权限。为了减少盗用,JWT的有效期应该设置得比较短。对于一些比较重要的权限,使用时应该再次对用户进行认证

  • JWT 适合一次性的命令认证,颁发一个有效期极短的 JWT,即使暴露了危险也很小,由于每次操作都会生成新的 JWT,因此也没必要保存 JWT,真正实现无状态

  • 为了减少盗用,JWT 不应该使用 HTTP 协议明码传输,要使用 HTTPS 协议传输

3. 使用加密算法时需要考虑的问题
  • 绝不要以明文存储密码

  • 永远使用 哈希算法 来处理密码,绝不要使用 Base64 或其他编码方式来存储密码,这和以明文存储密码是一样的,使用哈希,而不要使用编码。编码以及加密,都是双向的过程,而密码是保密的,应该只被它的所有者知道, 这个过程必须是单向的。哈希正是用于做这个的,从来没有解哈希这种说法,但是编码就存在解码,加密就存在解密

  • 绝不要使用弱哈希或已被破解的哈希算法,像 MD5 或 SHA1 ,只使用强密码哈希算法

  • 绝不要以明文形式显示或发送密码,即使是对密码的所有者也应该这样。如果你需要 “忘记密码” 的功能,可以随机生成一个新的 一次性的(这点很重要)密码,然后把这个密码发送给用户

只要关闭浏览器 ,session 真的就消失了?

4. 只要关闭浏览器 ,session 真的就消失了?

不对。浏览器关闭时,是不会主动去通知服务器的。之所以会有这种错觉,是大部分 session 机制都使用会话 cookie 来保存 sessionId,而关闭浏览器后这个 cookie 就消失了,再次连接服务器时也就无法找到原来的 session。如果服务器设置的 cookie 被保存在硬盘上,或者使用某种手段改写浏览器发出的 HTTP 请求头,把原来的 sessionId 发送给服务器,则再次打开浏览器仍然能够打开原来的 session。恰恰是由于关闭浏览器不会导致 session 被删除,迫使服务器为 session 设置了一个失效时间,当距离客户端上一次使用 session 的时间超过这个失效时间时,服务器就认为客户端已经停止了活动,才会把 session 删除以节省存储空间

10. 以前使用 Cookie 现在为什么改成 Token 了?

在前端发展史中, jQuery 时代的前端项目是作为后端项目的模块部署的。那时候前后端不分家,整个应用的入口是后端控制模板的渲染。在模板渲染前,后端会直接判断路由的权限来决定是否跳转。登录的时候,后端只需要设置 setCookie 这个 header,之后浏览器会自动把 cookie 写入到我们的浏览器存起来,然后当前域名在发送请求的时候都会自动带上这个 cookie。

但是,在现在这种前后端分离的场景下,通常前后端项目都会部署在不同的机器和服务器之上,Cookie 在跨域上有诸多的限制。所以在这种场景下,我们更愿意手动地去管理权限,于是就诞生了现在流行的基于 token 的权限解决方案,你也可以把 token 理解为我们手动管理的 cookie

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值