JWT 到底应该怎么用才对?
一、概述
JWT 全称为 JSON Web Token,是一份开源的标准协议,它定义了一种传输内容基于 JSON、轻量级、安全的数据传输方式。
二、细节
每个 JWT 都由 Header、Payload、Signature 3 部分组成,同时用点进行拼接,形式如下:
Header.Payload.Signature
Header
Header 部分是一个经过 Base64 编码后的 JSON 对象。对象的内容通常包括 2 个字段,形式如下:
{ "typ": "JWT", "alg": "HS256" }
其中,typ(全称为 type)指明当前的 Token 类型为 JWT,alg(全称为 algorithm)指明当前的签名算法是 HS256。
Payload
Payload 部分也是一个经过 Base64 编码后的 JSON 对象,对象的属性可以划分成 3 部分:保留字段、公共字段、私有字段。
保留字段是 JWT 内部声明,具有特殊作用的字段,包括
- iss(全称为 issuer),指明 JWT 是由谁签发的
- sub(全称为 subject),指明 JWT 的主题(也可理解为面向用户的类型)
- aud(全称为 audience),指明 JWT 希望谁签收
- exp(全称为 expiration time),指明 JWT 的过期时间,过期时间需大于签发时间
- nbf(全称为 not before time),指明 JWT 在哪个时间点生效
- iat(全称为 issued at time),指明 JWT 的签发时间
- jti(全称为 JWT ID),指明 JWT 唯一 ID,用于避免重放攻击
公共字段和私有字段都是用户可以任意添加的字段,区别在于公共字段是一些约定俗成,被普遍使用的字段,而私有字段更符合实际的应用场景。
当前已有的公共字段可以从 JSON Web Token Claims 中找到。
Payload 的结构形式如下:
{ "sub": "1234567890", "name": "John Doe", "iat": 1516239022 }
Signature
Signature 部分是 JWT 根据已有的字段生成的,它的计算方式是使用 Header 中定义的算法,使用用户定义的密钥,对经过 Base64 编码后的 Header 和 Payload 组成的字符串进行加密,形式如下:
HMACSHA256(base64(header) + '.' + base64(payload))
三、应用场景
业界普遍认可的应用场景主要有以下几种:
防止传输数据篡改
数据数据篡改指的是数据在传输过程中被截获,修改的行为。
JWT 本身可以使用加密算法对传输内容进行签名,即使数据被截获,也很难同时篡改签名和传输内容。
鉴权
鉴权指的是验证用户是否有访问系统的权利。
部分人使用 JWT 来取代传统的 Session + Cookie,理由是:
- 服务器开销小。使用 Session + Cookie 需要服务器缓存用户数据,而使用 JWT 则是直接将用户数据下发给客户端,每次请求附带一并发送给服务器。
- 扩展性好。服务器不缓存用户数据的好处是可以很方便的进行横向扩容
- 适用于单点登录。JWT 很适合做跨域情况下的单点登录
- 适用于搭配 RESTFul API 使用。基于 RESTFul 架构设计的 API 需遵循 RESTFul 的无状态原则,而基于 JWT 的鉴权恰恰是把状态转移到了客户端
基于 JWT 的鉴权一般处理逻辑是:
基于 JWT 的鉴权方案也存在一些争议:
- 服务器签发 JWT 后,并不能主动注销,若存在恶意请求则很难制止。其实可以通过 Token 黑名单的方式去解决。
- JWT 减少了服务器的开销,却增加了带宽的开销,JWT 生成的 Token 在体积上比 SessionID 大很多,意味着每次请求相比之前要携带更多的数据量。这个确实是这样,所以应该尽量只在 JWT 内放必要的数据。
- JWT 在鉴权方面并非完全优于 Session-Cookie,举个例子,SessionID 也可以通过签名的方式来防止篡改。
四、使用
以下使用 Node.js 和 JavaScript 演示 JWT 在鉴权方面的应用,涉及的库有:
如何生成 Token
Token 的生成一般是客户端发送登录请求,服务器使用密钥生成 Token 并放入响应体中,以下为服务端的 Token 生成逻辑。
// 文件位置:controller/v1/token.js const config = require('config') // 加载服务器配置 const jwt = require('jsonwebtoken') // 加载 jwt Node.js 语言实现 /** * 创建 Token 控制器 * @param {Object} ctx 请求上下文 */ async function create(ctx) { const username = ctx.request.body.username const password = ctx.request.body.password if (!username || !password) { ctx.throw(400, '参数错误') return } // 省略:用户名密码数据库校验 const user = { id: '5e54c02a2b073de564fe8034' } // 用户信息 const secret = config.get('secret') // 获取保存于配置中的密钥 const opt = { expiresIn: '2d' } // 设置 Token 过期时间为 2 天 ctx.body = jwt.sign(user, secret, opt) // 生成并返回 token } module.exports = { create, }
客户端携带 Token 进行请求
客户端一般情况下将 Token 放在 Http Header 的 Authorization 中,随请求发送给服务器。
// 文件位置:views/index.pug var request = axios.create({ baseURL: '/api/v1' }) // 创建请求实例 var token // 为了方便这里使用全局变量,正常情况下应该放入其他存储介质中,如,localStorage,此处省略获取逻辑 // 监听正常请求按钮单击事件,发起请求 document.querySelector('#normal').addEventListener('click', function() { if (!token) { alert('请登录') return } request.get('/users', { headers: { Authorization: 'Bearer ' + token, // 绑定 token 到 header 中 }, }).then(function({ data }) { document.querySelector('#response').innerHTML = JSON.stringify(data) }).catch(function(err) { console.log('Request Error: ', err) }) })
服务器如何验证 Token
验证操作一般放在服务器的中间件。
const config = require('config') // 加载服务器配置 const jwt = require('jsonwebtoken') // 加载 jwt Node.js 语言实现 // 定义中间件函数 module.exports = async (ctx, next) => { const path = ctx.url // 获取请求 URL const method = ctx.method.toLowerCase() // 获取请求方法 // 请求白名单,白名单中的请求不经过中间件 token 校验 const whiteList = [ { path: /^\/api\/v[1-9]\/tokens/, method: 'post' }, { path: /^\/api/, reverse: true }, // 非 /api 开头的资源都不需要经过请求校验 ] // 请求白名单检查函数 const checker = (i) => { const matchPath = i.path.test(path) const matchMethod = i.method ? i.method === method : true return (i.reverse ? !matchPath : matchPath) && matchMethod } // 白名单逻辑判断 if (whiteList.some(checker)) { await next() return } // 获取 http header 中的 token const token = (ctx.header.authorization || '').replace('Bearer ', '') // token 有效性校验 try { const data = jwt.verify(token, config.secret) ctx.userInfo = data } catch (e) { ctx.throw(400, 'Token 错误') } await next() }
查看完整代码请前往 GitHub 搜索用户 yo-squirrel
觉得写得不错可以关注下微信公众号「松鼠专栏」
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
一套手术机器人系统主要由哪些部分组成?
手术机器人,或者辅助手术机器人,是最近几年科研和投资的热点,受到越来越多投资人和创业者的青睐。目前的新冠疫情进一步让大家意识到了医疗行业机器换人的潜在价值。 美国、欧洲、日本的手术机器人临床应用已较为成熟,国内受制于技术起步晚、医疗认证周期较长等原因,目前进入临床应用的还不多。不过已有不少科研团队已经进入这个赛道,走在融资、研发、拿证的路上。应用场景涵盖多个领域,包括:骨科手术、神经外科、肿瘤消融、其它微创手术、整形外科、牙科、植发、按摩、针灸、减肥等。 如何研发一套完整的手术机器人?创业团队需要掌握哪些关键技术? 手术机器人的价值在于它的精准性和稳定性。因为人的眼睛误差远大于精密光学仪器,人的手也会抖动或疲劳而机械臂不会。所以,一套手术机器人首先需要一个机械臂来替代医生的手,或帮助医生进行手术器械的辅助定位。如下图: 其次,手术机器人只有手是不行的,它还需要一双眼睛。因为有了视觉,才可以保证手臂能够按照医生的手术规划进行精确的移动或旋转(六自由度移动)。这就需要一套三维空间定位系统,根据技术原理不同一般分为两种类型:红外光学定位系统、电磁定位系统。如下图所示: 好了,有了机器人的手臂...
- 下一篇
开发直播源码的计算机语言以及开发完成后所需要做的工作
开发直播网站源码的三种计算机语言直播网站源码开发所用的语言,根据未来运营平台不同,其选择也不同。一般来说,如果是PC后台,那么会采用PHP语言编写,如果是在安卓上开发,则选择Java语言,如果是在iOS上开发,则采用object-c语言编写。接下来,小编就简单介绍下这三种语言的优缺点,并适当的进行比较。一、JavaJava是一门计算机编程语言,和C++、Python等编程语言一样,Java如今依旧应用广泛。从我们日常用的安卓手机app到大部分网站到管理信息系统的应用服务器程序都是用Java这中语言来写的。之所以应用广泛,小编认为与Java能顺应面向对象这一主流的编程思想有很大的关系,将数据结构及其处理方法集成起来可以代码量,让程序员更多地把精力放在对程序的设计上,增加程序的功能性;同时了C类语言中指针、多继承等概念,引入了多线程、分布式与嵌入式概念,并且配合虚拟机的使用,让Java成为最佳的跨平台语言之一。二、PHPPHP是Hypertext Preprocessor的缩写,是一种计算机脚本语言。脚本语言不需要像编程语言那样在编译时生成二进制可执行文件,而是直接对写好的PHP代码执行以...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- CentOS关闭SELinux安全模块
- Eclipse初始化配置,告别卡顿、闪退、编译时间过长
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- Docker安装Oracle12C,快速搭建Oracle学习环境
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果
- SpringBoot2整合Redis,开启缓存,提高访问速度
- Windows10,CentOS7,CentOS8安装Nodejs环境
- CentOS7编译安装Cmake3.16.3,解决mysql等软件编译问题
- CentOS6,7,8上安装Nginx,支持https2.0的开启
- SpringBoot2整合Thymeleaf,官方推荐html解决方案