在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.hqwc.cn/news/91865.html

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

相关文章

小白学Go基础01-Go 语言的介绍

Go 语言对传统的面向对象开发进行了重新思考,并且提供了更高效的复用代码的手段。Go 语言还让用户能更高效地利用昂贵服务器上的所有核心,而且它编译大型项目的速度也很快。 用 Go 解决现代编程难题 Go 语言开发团队花了很长时间来解决当今软件开发人员…

是否在业务中使用大语言模型?

ChatGPT取得了巨大的成功,在短短一个月内就获得了1亿用户,并激发了企业和专业人士对如何在他们的组织中利用这一工具的兴趣和好奇心。 但LLM究竟是什么,它们如何使你的企业受益?它只是一种炒作,还是会长期存在? 在这篇文章中我…

Redis 复制(replica)

1. 是什么 1.1 官网地址 https://redis.io/docs/management/replication/ 1.2 一句话 1. 就是主从复制,master以写为主,slave以读为主 2. 当master数据变化的时候,自动将新的数据异步同步到其它slave数据库 2. 能干嘛 1. 读写分离 2. 容灾…

Ubuntu系统下配置 Qt Creator 输入中文、配置软件源的服务器地址、修改Ubuntu系统时间

上篇介绍了Ubuntu系统下搭建QtCreator开发环境。我们可以发现安装好的QtCreator不能输入中文,也没有中文输入法供选择,这里需要进行设置。 文章目录 1. 配置软件源的服务器地址2. 先配置Ubuntu系统语言,设置为中文3. 安装Fcitx插件&#xff…

学习笔记-ThreadLocal

ThreadLocal 什么是ThreadLocal? ThreadLocal 是线程本地变量类,在多线程并行执行过程中,将变量存储在ThreadLocal中,每个线程中都有独立的变量,因此不会出现线程安全问题。 应用举例 解决线程安全问题:例…

Maven的profiles多环境配置

一个项目通常都会有多个不同的运行环境,例如开发环境,测试环境、生产环境等。而不同环境的构建过程很可能是不同的,例如数据源配置、插件、以及依赖的版本等。每次将项目部署到不同的环境时,都需要修改相应的配置,这样…

<Linux>《SHELL脚本在crontab环境下执行失败问题处理》>> 探索SHELL运行模式和加载环境变量【实践+实验】

《SHELL脚本在crontab环境下执行失败问题处理》>> 探索SHELL运行模式和加载环境变量【实践实验】 1 现象描述2 分析3 解决方法4 深层研究4.1 shell4.2 shell脚本的环境变量4.3 shell脚本四种执行方法4.4 source 、.、./、bash 的区别4.5 shell常用的一些参数4.6 shell常见…

【Git】在idea中多分支开发如何——合并分支、处理冲突

博主简介:22级计算机科学与技术本科生一枚🌸博主主页:是瑶瑶子啦每日一言🌼: “人间总有一两风,填我十万八千梦” 目录 一、背景二、具体操作 一、背景 我当前开发的分支——hfy我想将subject分支的最新代码拉取&…

PID 算法

1.1 概述 比例(Proportion)积分(Integral)微分(Differential)控制器(PID控制器或三项控制器)是一种采用反馈的控制回路机制,广泛应用于工业控制系统和需要连续调制控制的…

说说Flink on yarn的启动流程

分析&回答 核心流程 FlinkYarnSessionCli 启动的过程中首先会检查Yarn上有没有足够的资源去启动所需要的container,如果有,则上传一些flink的jar和配置文件到HDFS,这里主要是启动AM进程和TaskManager进程的相关依赖jar包和配置文件。接着…

基于JAVAEE技术的ssm校园车辆管理系统源码和论文

基于JAVAEE技术的ssm校园车辆管理系统源码和论文105 开发工具:idea 数据库mysql5.7 数据库链接工具:navcat,小海豚等 技术:ssm 1.选题背景和意义 背景: 随着第二次工业革命后,内燃机的发明与完善,解…

有时间窗车辆路径问题(vehicle routing problems with time windows,VRPTW)学习实践与base案例代码开发

有时间窗车辆路径问题(Vehicle Routing Problems with Time Windows,VRPTW)是一类著名的组合优化问题,涉及在有限时间窗口约束下,有效地安排多个车辆的路径,以满足客户需求。 在VRPTW中,假设有…