在Nodejs中使用JWT进行鉴权

什么是 JSON Web Token(JWT)?

JSON Web Token(JWT)是一种用于在web上传递信息的标准,它以JSON格式表示信息,通常用于身份验证和授权。

JWT由三个部分组成:Header(头部)、Payload(负载)和Signature(签名)。它们用点号分隔开,形成了一个JWT令牌。

JWT 的基本结构

  • Header

Header(头部)是JWT结构的第一部分,它是一个包含关于令牌的元数据的JSON对象。Header通常包含两个主要字段:algtyp

alg(Algorithm)字段:这个字段指定了用于签名JWT的加密算法。它可以是以下之一:

  • HS256:HMAC-SHA256,使用密钥进行对称加密。
  • RS256:RSA-SHA256,使用RSA密钥对进行非对称加密。
  • ES256:ECDSA-SHA256,使用椭圆曲线数字签名算法进行非对称加密,等等。

typ(Type)字段:这个字段表示令牌的类型。对于JWT,这个字段的值通常是**JWT**,用于指示这是一个JSON Web Token。

一个简单的 JWT 头可以是下面这样:

{"typ":"JWT","alg":"HS256"}
  • Payload

Payload(负载)用于存储实际的用户数据和其他相关信息。Payload是一个包含键值对的JSON对象,它包含了有关JWT令牌的有用信息。

JWT 规定了7个官方字段:

- iss (issuer):签发人
- exp (expiration time):过期时间
- sub (subject):主题
- aud (audience):受众
- nbf (Not Before):生效时间
- iat (Issued At):签发时间
- jti (JWT ID):编号

除了官方字段,你还可以在这个部分定义私有字段,一个Payload如下所示:

 {"userId":"123","iss": "your_app","sub": "user123","role": "admin","exp": 1699999999}
  • Signature

JWT的Signature(签名)是JWT令牌的第三个部分,用于确保令牌的完整性和来源验证。Signature是通过将Header和Payload的组合(不包括分隔符**.**)与一个密钥进行加密或哈希生成的值。

Signature生成方式:

HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)

一个JWT示例

Header:
{"alg" : "HS256","typ" : "JWT"
}Payload:
{"id" : 123,"name" : "test"
}Secret: your_secret

Header(经过Base64编码):

eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9

Payload(经过Base64编码):

eyJpZCI6IDEyMywgIm5hbWUiOiAidGVzdCJ9

使用提供的Secret对原始的Header和原始的Payload进行加密生成Signature:

HMACSHA256(base64UrlEncode(header) + "." +base64UrlEncode(payload),"your_secret"
)

完整的的token需要吧这三部分拼起来如下:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MTIzLCJuYW1lIjoidGVzdCJ9.oMyOEgY
iZosc0HYCkIjrqh_DH3CLlmIkIjOe-icpTg8

在Nodejs中使用JWT

1,环境配置

我们先来配置一下环境,首先初始化一个package.json文件存放我们用到的npm包:

npm init -y

然后安装jsonwebtoken和express:
npm install express jsonwebtoken

2,创建一个基础的服务器

const express = require('express');
const jwt = require('jsonwebtoken');
const app = express();app.use(express.json());const PORT = 3000;
app.listen(PORT, () => {console.log(`Server is running on ${PORT} ...`);
});

这里我们使用了**express.json**这个中间件,express.json() 是一个 Express 中间件函数,用于解析传入请求体中的 JSON 数据。当客户端向服务器发送带有 JSON 数据的 POST 请求时,express.json() 中间件将从请求体中解析出 JSON 数据,并将其添加到到 req.body 上。

3,在登录之后下发token

// 用户数据
const users = [{ id: 1, username: "user1", password: "password1" },{ id: 2, username: "user2", password: "password2" }
];
const jwtSecretKey = "your_jwt_secret_key";// 登录之后生成 JWT token
app.post("/user/login", async (req, res) => {try {const { username, password } = req.body;const user = users.find(u => u.username === username && u.password === password);if (!user) {return res.status(401).json({ error: "用户名或密码错误" });}const payload = {userId: user.id,};//生成token 设置过期时间为 1 小时const token = await jwt.sign(payload, jwtSecretKey, { expiresIn: '1h' });res.json({ token });} catch (error) {res.status(500).json({ error: "登录失败" });}
});

为了演示,我们的用户数据是写死的

/user/login路由会在用户名和密码通过校验之后,使用jwt.sign生成一个token,并且设置过期时间为一个小时

jwt.sign 函数用于创建一个 JWT 令牌,它接受一个payload,并使用给定的密钥将其签名生成一个令牌字符串。

以下是 jwt.sign 的基本用法以及其参数:

jwt.sign(payload, secretOrPrivateKey, [options, callback])
  • payload:要存储在token中的数据,通常是一个 JavaScript 对象,可以包含任意信息。
  • secretOrPrivateKey:用于对令牌进行签名的密钥。
  • options(可选):一个包含选项的对象,用于配置生成的 JWT。常见的选项包括 expiresIn(过期时间)和 algorithm(签名算法)等。
  • callback(可选):一个回调函数,用于异步生成 JWT。

然后使用curl请求该路由,响应内容如下:
在这里插入图片描述

4,创建isLogin中间件

async function isLogin(req, res, next) {const tokenHeaderKey = 'Authorization';const token = req.header(tokenHeaderKey)if (!token) {return res.status(401).json({ error: "用户未登录" });}const verified = await jwt.verify(token, jwtSecretKey);if (verified) {next()} else {return res.status(401).json({ error: "无效的token" });}
}

isLogin 是一个用于验证用户是否已登录的中间件。它首先从请求头中获取 Authorization 字段的值,该值应该是 JWT 令牌。然后使用 jwt.verify 函数验证令牌的有效性。如果验证通过,将调用 next(),表示用户已登录,然后继续执行后续的路由处理程序。如果验证失败,返回 401 状态码,表示令牌无效。

5,创建需要身份验证的路由

// 获取用户信息
app.get("/user/:username", isLogin, async (req, res) => {const username = req.params.username;const user = users.find(u => u.username === username).map(u => ({ id: u.id, username: u.username}));res.json(user);
});

**/user/:username**是一个用于获取用户信息的路由。路由中的 :username 表示参数,表示用户的用户名。

我们在这个路由中添加了两个中间件,首先通过 isLogin 中间件验证用户是否已登录。如果用户已登录,才会进入到下一个中间件,然后根据用户名从 users 数组中查找用户信息并作为响应。

然后使用curl请求该路由,就能拿到用户信息:

在这里插入图片描述

6,使用axios携带token请求用户信息

import axios from "axios";
const token = localStorage.getItem("token");
const url = "http://localhost:3000/user/your_username"
const headers = {"Authorization": token,"Content-Type": "application/json","Accept": "application/json",
};
const getUserInfo = () => {axios.get(url, { headers: headers }).then((response) => {console.log(response);}).catch((error) => {console.log(error);});
}

在前端我们一般会使用axios来发起请求,只要把token的值放在http header中的Authorization字段即可。当然除了放在Authorization之外,也可以放在其他header字段中,不过后端也需要从对应的header字段取token。

完整代码:

const express = require('express');
const jwt = require('jsonwebtoken');
const app = express();app.use(express.json());const PORT = 3000;
app.listen(PORT, () => {console.log(`Server is up and running on ${PORT} ...`);
});// 用户数据
const users = [{ id: 1, username: "user1", password: "password1" },{ id: 2, username: "user2", password: "password2" }
];
const jwtSecretKey = "your_jwt_secret_key";// 登录之后生成 JWT token
app.post("/user/login", async (req, res) => {try {const { username, password } = req.body;const user = users.find(u => u.username === username && u.password === password);if (!user) {return res.status(401).json({ error: "用户名或密码错误" });}const payload = {userId: user.id,};//生成token 设置过期时间为 1 小时const token = await jwt.sign(payload, jwtSecretKey, { expiresIn: '1h' });res.json({ token });} catch (error) {res.status(500).json({ error: "登录失败" });}
});async function isLogin(req, res, next) {const tokenHeaderKey = 'Authorization';const token = req.header(tokenHeaderKey)if (!token) {return res.status(401).json({ error: "用户未登录" });}const verified = await jwt.verify(token, jwtSecretKey);if (verified) {next()} else {return res.status(401).json({ error: "无效的token" });}
}
// 获取用户信息
app.get("/user/:username", isLogin, async (req, res) => {const username = req.params.username;const user = users.map(u => ({ id: u.id, username: u.username})).find(u => u.username === username)res.json(user);
});

总结

这篇文章我们介绍了JWT原理以及在nodejs中使用JWT 进行鉴权,除了JWT之外还可以使用session管理用户状态,感兴趣的可以👇这里:https://blog.csdn.net/AC_greener/article/details/130036699

参考文章:

https://github.com/auth0/node-jsonwebtoken

https://www.ruanyifeng.com/blog/2018/07/json_web_token-tutorial.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/115107.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

软件测试Day4|软件测试理论02

目录 6. 测试用例基础6.1 测试用例的定义6.2 测试用例要素6.3 测试用例设计和编写的作用 7. 黑盒测试用例设计方法7.1 用例设计方法分类7.2 测试数据选择7.2.1 等价类划分(1)等价类划分原理(2)确定等价类的原则(3&…

摆动序列【贪心算法】

摆动序列 如果连续数字之间的差严格地在正数和负数之间交替,则数字序列称为 摆动序列 。第一个差(如果存在的话)可能是正数或负数。仅有一个元素或者含两个不等元素的序列也视作摆动序列。 class Solution {public int wiggleMaxLength(int…

zabbix安装部署

前期准备:安装mysql数据库和nginx 一、下载zabbix rpm -Uvh https://repo.zabbix.com/zabbix/4.4/rhel/7/x86_64/zabbix-release-4.4-1.el7.noarch.rpm yum-config-manager --enable rhel-7-server-optional-rpms yum install epel-release numactl yum install…

Spring Cloud Gateway的快速使用

环境前置搭建Nacos&#xff1a;点击跳转 Spring Cloud Gateway Docs 新建gateway网关模块 pom.xml导入依赖 <!-- 网关 --> <dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-gateway</artifact…

Spring MVC 学习总结

学习目标 了解 Spring MVC 是什么&#xff0c;为什么要使用它或者说它能解决什么问题&#xff0c;其与 Spring 是什么关系。理解为什么配置 Spring MVC 的前端控制器的映射路径为 “/” 会导致静态资源访问不了&#xff0c;掌握怎么处理这个问题。掌握基于注解方式使用 Spring…

Zookeeper的使用

一、Zookeeper简介 分布式协调框架&#xff0c;小型的树形结构数据共享储存系统。 zookeeper的应用场景 集群管理 注册中心 配置中心 发布者将数据发布到ZooKeeper一系列节点上面&#xff0c;订阅者进行数据订阅&#xff0c;当数据有变化时&#xff0c;可及时得到数据的变…

算法通关村——从40个亿中产生一个不存在的整数

Titile: 海量数据场景下的热门算法题 从40个亿中产生一个不存在的整数 题目要求&#xff1a;给定一个输入文件&#xff0c;包含40亿个非负整数&#xff0c;请设计一个算法&#xff0c;产生一个不存在该文件中的整数&#xff0c;假设你有1GB的内存来完成这项任务。 进阶&…

MySQL 5种索引应用

文章目录 简介一、聚集索引二、唯一索引三、聚集索引和唯一索引对比四、非唯一&#xff08;普通&#xff09;索引五、全文索引六、组合索引七、索引验证总结 简介 在本篇文章中&#xff0c;我们将学习MySQL中5种不同类型的索引及其应用场景&#xff0c;以及它们的优缺点。 一…

基于YOLOv8分割模型实现垃圾识别

基于YOLOv8分割模型实现垃圾识别 本文首发于公众号【DeepDriving】&#xff0c;欢迎关注。 0. 引言 YOLOv8是Ultralytics开源的一个非常火的AI算法&#xff0c;目前支持目标检测、实例分割、姿态估计等任务。如果对YOLOv8的安装和使用还不了解的可以参考我之前写的这篇文章&am…

从C语言到C++_36(智能指针RAII)auto_ptr+unique_ptr+shared_ptr+weak_ptr

目录 1. 智能指针的引入_内存泄漏 1.1 内存泄漏 1.2 如何避免内存泄漏 2. RAII思想 2.1 RAII解决异常安全问题 2.2 智能指针原理 3. auto_ptr 3.1 auto_ptr模拟代码 4. unique_ptr 4.1 unique_ptr模拟代码 5. shared_ptr 5.1 shared_ptr模拟代码 5.2 循环引用 6.…

(笔记六)利用opencv进行图像滤波

&#xff08;1&#xff09;自定义卷积核图像滤波 import numpy as np import matplotlib.pyplot as plt import cv2 as cvimg_path r"D:\data\test6-6.png" img cv.imread(img_path)# 图像滤波 ker np.ones((6, 6), np.float32)/36 # 构建滤波器&#xff08;卷积…

Stable Diffusion中的ControlNet插件

文章目录 ControlNet的介绍及安装ControlNet的介绍ControlNet的安装 ControlNet的功能介绍ControlNet的应用与演示 ControlNet的介绍及安装 ControlNet的介绍 ControlNet 的中文就是控制网&#xff0c;本质上是Stable Diffusion的一个扩展插件&#xff0c;在2023年2月份由斯坦…

supervisorctl(-jar)启动配置设置NACOS不同命名空间

背景 由于需要在上海服务器上面配置B测试环境&#xff0c;原本上面已有A测试环境&#xff0c;固需要将两套权限系统分开 可以使用不同的命名空间来隔离启动服务 注&#xff1a;本文章均不涉及公司机密 1、新建命名空间 命名空间默认会有一个public&#xff0c;并且不能删除&a…

数据结构入门 — 栈

本文属于数据结构专栏文章&#xff0c;适合数据结构入门者学习&#xff0c;涵盖数据结构基础的知识和内容体系&#xff0c;文章在介绍数据结构时会配合上动图演示&#xff0c;方便初学者在学习数据结构时理解和学习&#xff0c;了解数据结构系列专栏点击下方链接。 博客主页&am…

Linux 忘记密码解决方法

很多朋友经常会忘记Linux系统的root密码&#xff0c;linux系统忘记root密码的情况该怎么办呢&#xff1f;重新安装系统吗&#xff1f;答案是不需要进入单用户模式更改一下root密码即可。 步骤如下&#xff1a; 重启linux系统 3 秒之内要按一下回车&#xff0c;出现如下界面 …

VUE笔记(十)Echarts

一、Echarts简介 1、什么是echarts ECharts是一款基个基于 JavaScript 的开源可视化图表库 官网地址&#xff1a;Apache ECharts 国内镜像&#xff1a;ISQQW.COM x ECharts 文档&#xff08;国内同步镜像&#xff09; - 配置项 示例&#xff1a;echarts图表集 2、第一个E…

滑动窗口实例4(将x减到0的最小操作数)

题目&#xff1a; 给你一个整数数组 nums 和一个整数 x 。每一次操作时&#xff0c;你应当移除数组 nums 最左边或最右边的元素&#xff0c;然后从 x 中减去该元素的值。请注意&#xff0c;需要 修改 数组以供接下来的操作使用。 如果可以将 x 恰好 减到 0 &#xff0c;返回 …

全套解决方案:基于pytorch、transformers的中文NLP训练框架,支持大模型训练和文本生成,快速上手,海量训练数据!

全套解决方案&#xff1a;基于pytorch、transformers的中文NLP训练框架&#xff0c;支持大模型训练和文本生成&#xff0c;快速上手&#xff0c;海量训练数据&#xff01; 1.简介 目标&#xff1a;基于pytorch、transformers做中文领域的nlp开箱即用的训练框架&#xff0c;提…

WebGPU加载Wavefront .OBJ模型文件

在开发布料模拟之前&#xff0c;我想使用 WebGPU 开发强大的代码基础。 这就是为什么我想从 Wavefront .OBJ 文件加载器开始渲染 3D 模型。 这样&#xff0c;我们可以快速渲染 3D 模型&#xff0c;并构建一个简单而强大的渲染引擎来完成此任务。 一旦我们有了扎实的基础&#x…

视频文件损坏无法播放如何修复?导致视频文件损坏的原因

如果我们遇到因视频文件损坏而无法正常播放&#xff0c;我们该怎么办&#xff1f;这种情况通常意味着视频文件已经损坏。我们不能访问、编辑或使用它们。那么应该用什么正确的工具和修复程序来修复视频呢&#xff1f; 视频文件损坏的原因 了解视频损坏如何修复之前&#xff0c…