Node.js 应用:Koa2 使用 JWT 进行鉴权

2023-11-17

前言

在前后端分离的开发中,通过 Restful API 进行数据交互时,如果没有对 API 进行保护,那么别人就可以很容易地获取并调用这些 API 进行操作。那么服务器端要如何进行鉴权呢?

Json Web Token 简称为 JWT,它定义了一种用于简洁、自包含的用于通信双方之间以 JSON 对象的形式安全传递信息的方法。JWT 可以使用 HMAC 算法或者是 RSA 的公钥密钥对进行签名。

说得好像跟真的一样,那么到底要怎么进行认证呢?

jwt流程图

首先用户登录时,输入用户名和密码后请求服务器登录接口,服务器验证用户名密码正确后,生成token并返回给前端,前端存储token,并在后面的请求中把token带在请求头中传给服务器,服务器验证token有效,返回正确数据。

既然服务器端使用 Koa2 框架进行开发,除了要使用到 jsonwebtoken 库之外,还要使用一个 koa-jwt 中间件,该中间件针对 Koa 对 jsonwebtoken 进行了封装,使用起来更加方便。下面就来看看是如何使用的。

生成token

这里注册了个 /login 的路由,用于用户登录时获取token。

const router = require('koa-router')();
const jwt = require('jsonwebtoken');
const userModel = require('../models/userModel.js');

router.post('/login', async (ctx) => {
    const data = ctx.request.body;
    if(!data.name || !data.password){
        return ctx.body = {
            code: '000002',
            data: null,
            msg: '参数不合法'
        }
    }
    const result = await userModel.findOne({
        name: data.name,
        password: data.password
    })
    if(result !== null){
        const token = jwt.sign({
            name: result.name,
            _id: result._id
        }, 'my_token', { expiresIn: '2h' });
        return ctx.body = {
            code: '000001',
            data: token,
            msg: '登录成功'
        }
    }else{
        return ctx.body = {
            code: '000002',
            data: null,
            msg: '用户名或密码错误'
        }
    }
});

module.exports = router;

在验证了用户名密码正确之后,调用 jsonwebtoken 的 sign() 方法来生成token,接收三个参数,第一个是载荷,用于编码后存储在 token 中的数据,也是验证 token 后可以拿到的数据;第二个是密钥,自己定义的,验证的时候也是要相同的密钥才能解码;第三个是options,可以设置 token 的过期时间。

获取token

接下来就是前端获取 token,这里是在 vue.js 中使用 axios 进行请求,请求成功之后拿到 token 保存到 localStorage 中。这里登录成功后,还把当前时间存了起来,除了判断 token 是否存在之外,还可以再简单的判断一下当前 token 是否过期,如果过期,则跳登录页面

submit(){
    axios.post('/login', {
        name: this.username,
        password: this.password
    }).then(res => {
        if(res.code === '000001'){
            localStorage.setItem('token', res.data);
            localStorage.setItem('token_exp', new Date().getTime());
            this.$router.push('/');
        }else{
            alert(res.msg);
        }
    })
}

然后请求服务器端API的时候,把 token 带在请求头中传给服务器进行验证。每次请求都要获取 localStorage 中的 token,这样很麻烦,这里使用了 axios 的请求拦截器,对每次请求都进行了取 token 放到 headers 中的操作。

axios.interceptors.request.use(config => {
    const token = localStorage.getItem('token');
    config.headers.common['Authorization'] = 'Bearer ' + token;
    return config;
})

验证token

通过 koa-jwt 中间件来进行验证,用法也非常简单

const koa = require('koa');
const koajwt = require('koa-jwt');
const app = new koa();

// 错误处理
app.use((ctx, next) => {
    return next().catch((err) => {
        if(err.status === 401){
            ctx.status = 401;
            ctx.body = 'Protected resource, use Authorization header to get access\n';
        }else{
            throw err;
        }
    })
})

app.use(koajwt({
    secret: 'my_token'
}).unless({
    path: [/\/user\/login/]
}));

通过 app.use 来调用该中间件,并传入密钥 {secret: 'my_token'},unless 可以指定哪些 URL 不需要进行 token 验证。token 验证失败的时候会抛出401错误,因此需要添加错误处理,而且要放在 app.use(koajwt()) 之前,否则不执行。

如果请求时没有token或者token过期,则会返回401。

解析koa-jwt

我们上面使用 jsonwebtoken 的 sign() 方法来生成 token 的,那么 koa-jwt 做了些什么帮我们来验证 token。

resolvers/auth-header.js

module.exports = function resolveAuthorizationHeader(ctx, opts) {
    if (!ctx.header || !ctx.header.authorization) {
        return;
    }
    const parts = ctx.header.authorization.split(' ');
    if (parts.length === 2) {
        const scheme = parts[0];
        const credentials = parts[1];
        if (/^Bearer$/i.test(scheme)) {
            return credentials;
        }
    }
    if (!opts.passthrough) {
        ctx.throw(401, 'Bad Authorization header format. Format is "Authorization: Bearer <token>"');
    }
};

在 auth-header.js 中,判断请求头中是否带了 authorization,如果有,将 token 从 authorization 中分离出来。如果没有 authorization,则代表了客户端没有传 token 到服务器,这时候就抛出 401 错误状态。

verify.js

const jwt = require('jsonwebtoken');

module.exports = (...args) => {
    return new Promise((resolve, reject) => {
        jwt.verify(...args, (error, decoded) => {
            error ? reject(error) : resolve(decoded);
        });
    });
};

在 verify.js 中,使用 jsonwebtoken 提供的 verify() 方法进行验证返回结果。jsonwebtoken 的 sign() 方法来生成 token 的,而 verify() 方法则是用来认证和解析 token。如果 token 无效,则会在此方法被验证出来。

index.js

const decodedToken = await verify(token, secret, opts);
if (isRevoked) {
    const tokenRevoked = await isRevoked(ctx, decodedToken, token);
    if (tokenRevoked) {
        throw new Error('Token revoked');
    }
}
ctx.state[key] = decodedToken;  // 这里的key = 'user'
if (tokenKey) {
    ctx.state[tokenKey] = token;
}

在 index.js 中,调用 verify.js 的方法进行验证并解析 token,拿到上面进行 sign() 的数据 {name: result.name, _id: result._id},并赋值给 ctx.state.user,在控制器中便可以直接通过 ctx.state.user 拿到 name_id

安全性

  • 如果 JWT 的加密密钥泄露的话,那么就可以通过密钥生成 token,随意的请求 API 了。因此密钥绝对不能存在前端代码中,不然很容易就能被找到。
  • 在 HTTP 请求中,token 放在 header 中,中间者很容易可以通过抓包工具抓取到 header 里的数据。而 HTTPS 即使能被抓包,但是它是加密传输的,所以也拿不到 token,就会相对安全了。

总结

这上面就是 jwt 基本的流程,这或许不是最完美的,但在大多数登录中使用已经足够了。
上面的代码可能不够具体,这里使用 Koa + mongoose + vue.js 实现的一个例子 : jwt-demo,可以做为参考。

更多文章:lin-xin/blog

转载于:https://www.cnblogs.com/linxin/p/9491342.html

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Node.js 应用:Koa2 使用 JWT 进行鉴权 的相关文章

  • Web 串行 API - 未捕获(承诺中)DOMException:无法打开串行端口/所需成员 baudRate 未定义

    下面的代码可以在我的 Xubuntu 机器上运行 但现在我在 Kubuntu 上 它不再工作了 它不会打开端口 Arduino IDE 工作正常 可以向开发板写入代码 并且我可以在 Chrome 中选择设备 Arduino Uno 但当我尝
  • 在 Vue.js 中从父组件执行子方法

    目前 我有一个 Vue js 组件 其中包含其他组件的列表 我知道使用 vue 的常见方式是将数据传递给孩子 并从孩子向父母发出事件 但是 在这种情况下 我想在子组件中的按钮出现时执行子组件中的方法 parent被点击 哪种方法最好 一种建
  • 解析“流”JSON

    我在浏览器中有一个网格 我想通过 JSON 将数据行发送到网格 但浏览器应该在接收到 JSON 时不断解析它 并在解析时将行添加到网格中 换句话说 在接收到整个 JSON 对象后 不应将行全部添加到网格中 应该在接收到行时将其添加到网格中
  • 使用模数按字母顺序对列表进行排序

    我在获取元素列表并按字母顺序对它们进行排序方面没有任何问题 但我很难理解如何使用模数来做到这一点 更新 这是按我的方式工作的代码 但是 我更喜欢下面提供的答案的可重用性 因此接受了该答案
  • 我想检查 $('#td1').text() === "x" 是否?

    我想检查innerHtml是否有X或O 所以我不能再次添加任何其他东西 但它不起作用 添加检查代码后它就停止了 我在这里尝试做一个简单的XO游戏来更熟悉javascript和jquery 我也不确定是否可以用 jQuery 做到这一点
  • jQuery AJAX 调用 Java 方法

    使用 jQuery AJAX 我们可以调用特定的 JAVA 方法 例如从 Action 类 该 Java 方法返回的数据将用于填充一些 HTML 代码 请告诉我是否可以使用 jQuery 轻松完成此操作 就像在 DWR 中一样 此外 对于
  • 在 Wordpress 站点中进行 AJAX 调用时出现问题

    我在使用 Wordpress 站点功能的 AJAX 部分时遇到了一些问题 该功能接受在表单上输入的邮政编码 使用 PHP 函数来查找邮政编码是否引用特定位置并返回到该位置的永久链接 我的第一个问题是关于我构建的表单 现在我的表单操作是空白的
  • 将 MOXy 设置为 JAXB 提供程序,而在同一包中没有属性文件

    我正在尝试使用 MOXy 作为我的 JAXB 提供程序 以便将内容编组 解组到 XML JSON 中 我创建了 jaxb properties 文件 内容如下 javax xml bind context factory org eclip
  • 如何抑制窗口鼠标滚轮滚动...?

    我正在开发嵌入页面中的画布应用程序 我有它 因此您可以使用鼠标滚轮放大绘图 但不幸的是 这会滚动页面 因为它是文章的一部分 当我在 dom 元素上滚动鼠标滚轮时 是否可以阻止鼠标滚轮在窗口上滚动 附加鼠标滚轮 不是 Gecko DOMMou
  • 为什么是 javascript:history.go(-1);无法在移动设备上工作?

    首先 一些背景 我有一个向用户呈现搜索页面 html 表单 的应用程序 填写标准并单击 搜索 按钮后 结果将显示在标准部分下方 在结果列表中 您可以通过单击将您带到新页面的链接来查看单个结果的详细信息 在详细信息页面中 我添加了一个 返回结
  • 标签获取 href 值

    我有以下 html div class threeimages a img alt Australia src Images Services 20button tcm7 9688 gif a div class text h2 a hre
  • 如何在谷歌地图android上显示多个标记

    我想在谷歌地图android上显示带有多个标记的位置 问题是当我运行我的应用程序时 它只显示一个位置 标记 这是我的代码 public class koordinatTask extends AsyncTask
  • 使用 Ajax.Request 将 JSON 从浏览器传递到 PHP 的最佳方法

    您好 我有一个 JSON 对象 它是一个二维数组 我需要使用 Ajax Request 将其传递给 PHP 我知道的唯一方法 现在我使用js函数手动序列化我的数组 并获取以下格式的数据 s 1 d 3 4等 我的问题是 有没有办法更直接 有
  • 使用 AsyncTask 传递值

    我一直在努力解决这个问题 但我已经到了不知道该怎么办的地步 我想做的是使用一个类下载文件并将其解析为字符串 然后将该字符串发送到另一个类来解析 JSON 内容 所有部件都可以单独工作 并且我已经单独测试了所有部件 我只是不知道如何将值发送到
  • 如何使输入字段和提交按钮变灰

    我想变灰这两件事 http doorsplit heroku com 歌曲输入字段和提交按钮 直到用户输入艺术家 有没有一种简单的方法可以通过 JQuery 来做到这一点 艺术家输入字段的id是 request artist 你可以这样做
  • 提交表单并重定向页面

    我在 SO 上看到了很多与此相关的其他问题 但没有一个对我有用 我正在尝试提交POST表单 然后将用户重定向到另一个页面 但我无法同时实现这两种情况 我可以获取重定向或帖子 但不能同时获取两者 这是我现在所拥有的
  • Laravel 中只向登录用户显示按钮

    如果我以 John 身份登录 如何才能只显示 John 的红色按钮而不显示 Susan 的红色按钮 测试系统环境 Win10 Laravel5 4 Mysql5 7 19 table class table table responsive
  • 为什么我不能在 AngularJS 中使用 data-* 作为指令的属性名称?

    On the t他的笨蛋 http plnkr co edit l3KoY3 p preview您可以注意到属性名称模式的奇怪行为data 在指令中 电话 Test of data named attribute br
  • Javascript 纪元时间(以天为单位)

    我需要以天为单位的纪元时间 迄今为止 我已经看到过有关如何翻译它的帖子 但几天后就没有了 我对纪元时间很不好 我怎么能得到这个 我需要以天为单位的纪元时间 我将解释为您想要自纪元以来的天数 纪元本身是第 0 天 或第 1 天的开始 无论您如
  • 如何仅在最后一个
  • 处给出透明六边形角度?
  • 我必须制作这样的菜单 替代文本 http shup com Shup 330421 1104422739 My Desktop png http shup com Shup 330421 1104422739 My Desktop png

随机推荐

  • C++ 游戏开发 打地鼠详解 (含.h文件和.cpp文件)

    基于easyx的打地鼠游戏 期末大作业 C语言版本请见 C语言 打地鼠游戏 超级详解 各个函数与算法 设计思路与流程 一 游戏简介 游戏简介 疯狂打地鼠 是一款经典的单机休闲益智类小游戏 调皮的小地鼠们又出来活动了 你需要做的就是将他们砸回
  • 关于Chrome浏览器这点儿事儿(一):基本尝试修改Chrome启动参数实现全屏禁用双指触摸缩放

    最近 刚上任项目boss 接触客户第一个需求便是浏览器需求 需求如下 其中标注红色的需求 是让我花了几十种方式尝试的主要源 自己做过C 知道可以嵌入外壳 有很多种方案 当然了参考了一位仁兄的文章 https www cnblogs com
  • 二进制安装mysql

    下载安装包 下载mysql二进制安装包 yum y install lrzsz numactl wget http mirrors 163 com mysql Downloads MySQL 5 7 mysql test 5 7 38 li
  • error C2676: 二进制“<”:“const _Ty”不定义该运算符或到预定义运算符可接收的类型的转换

    项目场景 C 中写优先级序列 序列内存的是自定义的结构体 在自定义比较方式的时候系统报错 error C2676 二进制 lt const Ty 不定义该运算符或到预定义运算符可接收的类型的转换 with Ty Node 问题描述 prie
  • Javascript使用turndown 将html 转为md

    1 安装turndown npm i turndown 2 使用trundown并保存为md文件 htmlToMarkdown let down new turndown let md down turndown this html let
  • bin、hex、elf、axf文件解析

    冰冻三尺非一日之寒 滴水穿石非一日之功 文章目录 引言 文件分类 1 bin文件 2 hex文件 3 axf文件 4 elf文件 总结 参考资料 深度理解编译过程 参考资料 深度理解编译文件 引言 bin hex elf axf作为嵌入式开
  • python数据可视化07

    1 绘制等高线图 import numpy as np import matplotlib pyplot as plt def calcu elevation x1 y1 h 1 x1 2 x1 5 y1 3 np exp x1 2 y1
  • 每日一题: 扑克牌中的顺子(C++)

    题目描述 从扑克牌中随机抽5张牌 判断是不是一个顺子 即这5张牌是不是连续的 2 10为数字本身 A为1 J为11 Q为12 K为13 而大 小王为 0 可以看成任意数字 A 不能视为 14 示例 1 输入 1 2 3 4 5 输出 Tru
  • Torchserve打包和部署你训练的深度学习模型

    一 Torchserve介绍 Torchserve是Facebooke公司开发的在线深度学习模型部署框架 它可以很方便的部署pytorch的深度学习模型 读者可以访问Github地址获取最新功能和详细说明 官方地址https github
  • Shell 中常用 Date 日期的计算

    在使用 Crontab 定时任务和 Shell 脚本切割 Nginx 日志文件时 要用到时间戳 当月 上月 下月 上月初 上月末 下月初 下月末等等 其中有些日期不能直接获取 需要经过一定的计算才能得到 一 Date 基础格式化 格式 输出
  • JVM性能优化 —— 类加载器,手动实现类的热加载

    一 类加载的机制的层次结构 每个编写的 java 拓展名类文件都存储着需要执行的程序逻辑 这些 java 文件经过Java编译器编译成拓展名为 class 的文件 class 文件中保存着Java代码经转换后的虚拟机指令 当需要使用某个类时
  • qt5.5程序打包发布以及依赖

    玩qt5也有一段时间了 惭愧的是一直没有好好的发布过程序 因为写的都是小程序没啥需要用到发布 而且qt也说不上很熟悉 本来打算到基本掌握qt之后再来研究研究怎么打包程序 最近晚上的空闲时间多了 闲着也是闲着 于是便来试试 在网上搜索了一下资
  • Newifi3(新路由3)刷潘多拉(Pandora)固件

    最近在淘宝入手了一个二手的newifi3 主要是因为它内存大 而且性价比相当高 512M的ddr2和32M的flash买下来才100左右 下面介绍如何刷 Pandora固件 步骤 1 找一根网线 一端插入路由器wan口 一端插入电脑 把电脑
  • Python爬虫从入门到精通:(11)数据解析_站长素材图片的爬取(爬取俄罗斯高清图片名称和地址)* _Python涛哥

    站长素材图片的爬取 爬取俄罗斯高清图片名称和地址 地址 https sc chinaz com tupian eluosi html 我们先来分析下页面 获取图片名称和地址定位 我们很容易获取到了图片名称和地址是在 div div div
  • SQL语句截取字段某指定字符的前半段/后半段内容

    最近项目中遇到一个小问题 需要从数据库中取出对应数据 并根据某个字段中的前半段内容进行排序 搜索资料后得以解决 现将解决方法记录如下 最初的查询SQL SELECT file name sort FROM base annexesfile
  • 盛最多水的容器

    给定一个长度为 n 的整数数组 height 有 n 条垂线 第 i 条线的两个端点是 i 0 和 i height i 找出其中的两条线 使得它们与 x 轴共同构成的容器可以容纳最多的水 返回容器可以储存的最大水量 说明 你不能倾斜容器
  • Palindrome subsequence【区间DP+冗斥】

    题目链接HDU 4632 题目让我们求给定的一段字符串上回文串的长度 一个数也算是回文串 于是我就想怎样去找其中的规律 我举了些例子 先是从相同的字符串开始举例 aaa 对于aaa dp 1 1 1 dp 2 2 1 dp 1 2 dp 1
  • 遥感+python 目录

    遥感 python 1 遥感影像预处理 环境搭建 辐射定标 大气校正 RPC校正 重投影 2 遥感影像样本读取 CSV 格式样本读取 XLS 格式样本读取 TIFF 格式样本读取 SHAPE 格式样本读取 3 遥感影像分类器构建 KNN分类
  • Linux——进程信号的发送

    目录 一 信号发送的概念 首先来讲几个发送术语 它有三种情况 注意 二 信号在内核中的表示示意图 三 信号捕捉 所以总结一下 此时 会出现这样一个疑问 操作系统是如何得知现在被执行的进程是用户态还是内核态 问题2 CPU在执行某个进程时是如
  • Node.js 应用:Koa2 使用 JWT 进行鉴权

    前言 在前后端分离的开发中 通过 Restful API 进行数据交互时 如果没有对 API 进行保护 那么别人就可以很容易地获取并调用这些 API 进行操作 那么服务器端要如何进行鉴权呢 Json Web Token 简称为 JWT 它定