在Nodejs中使用JWT进行鉴权

news2024/9/20 20:44:44

什么是 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.coloradmin.cn/o/948452.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

五、MySQL(DML)如何连接到DataGrip?并显示所有数据库?

前提:已经配置好DataGrip,并创建好一个项目: 1、选择数据库: 点击左上角加号,再选择数据源,选择MySQL数据源: 2、填写信息: 用户栏填写:root 密码填写:你…

算法设计 || 第9题:0-1背包问题动态规划(手写例题+源代码)

(一)背包问题知识点: (二)经典测试题: 已知n8种,每种一件。背包最大负载M110。 重量w和价值v如下表,怎样装价值最大?贪心算法 求X[N]最优解,写出求解过程;强化为0/1背包…

基于clip驱动的器官分割和肿瘤检测通用模型

论文:https://arxiv.org/abs/2301.00785 我看这篇主要是看看MRI的多模态融合方法的,所以会略一些东西,感兴趣细节的就翻原文好嘞 摘要 越来越多的公共数据集在自动器官分割和肿瘤检测方面显示出显著的影响。然而,由于每个数据集…

冠达管理:股票减持是什么意思?2023减持新规?

在a股商场上,大股东一般会进行大宗买卖、减持来影响股价,那么,股票减持是什么意思?2023减持新规?下面冠达管理为我们准备了相关内容,以供参阅。 ​ 股票减持是指上市公司持股比例较高的股东出售所持股份以…

ARM-M0 + 24bit 高精度ADC,采样率4KSPS,国产新品,传感器首选

ARM-M0内核MCU 内置24bit ADC ,采样率4KSPS flash 64KB,SRAM 32KB 适用于传感器,电子秤,体脂秤等等

【爬虫】5.6 Selenium等待HTML元素

任务目标 在浏览器加载网页的过程中,网页的有些元素时常会有延迟的现象,在HTML元素还没有准备好的情况下去操作这个HTML元素必然会出现错误,这个时候Selenium需要等待HTML元素。例如:上节实例中出现的select的下拉框元素&#xff…

htmx-使HTML更强大

‍本文作者是360奇舞团开发工程师 htmx 让我们先来看一段俳句: javascript fatigue: longing for a hypertext already in hand 这个俳句很有意思,是开源项目htmx文档中写的,意思是说,我们已经有了超文本,为什么还要去使用javascr…

1、Spring是什么?

Spring 是一款主流的 Java EE 轻量级开源框架 。 框架 你可以理解为是一个程序的半成品,它帮我们实现了一部分功能,用这个框架我们可以减少代码的实现和功能的开发。 开源 也就是说,它开放源代码。通过源代码,你可以看到它是如何…

【问题思考总结】为什么B树中的搜索可以在分支结点上结束,而B+树必须到叶节点上才能结束?

问题提出 在刷到B树的时候,发现王道书上写B树非叶子结点仅仅起到索引作用,没有关键字对应记录的存储地址。 然而,观察B树的存储结构,我们发现,其中对于每个结点,也仅有结点的关键字信息和指向子树的指针…

SpringBoot—日志

目录 日志使用日志日志级别设置日志级别设置分组指定日志文件路径日志切割归档使用第三方日志框架log4j2配置文件【分级存储】logback配置文件【分级存储】 实例代码 日志 使用日志 给controller添加日志信息 要给controller类上添加Slf4j注解,然后使用log.info(…

Android 开发中的sdkmanager 操作说明(Delphi适用)

目录 sdkmanager 说明: 用法: 列出已安装和可用的软件包 安装软件包 更新所有已安装的软件包 接受许可 选项 sdkmanager 说明: sdkmanager 是一个命令行工具,您可以用它来查看、安装、更新和卸载 Android SDK 的软件包。如…

前端文件、图片直传OOS、分片上传、el-upload上传(vue+elementUI)

前言:基于天翼云的面相对象存储(Object-Oriented Storage,OOS),实现小文件的直接上传,大文件的分片上传。 开发文档地址:网址 上传之前的相关操作:注册账户,创建 AccessKeyId 和 AccessSecretKey之后&…

Private market:借助ZK实现的任意计算的trustless交易

1. 引言 Private market,借助zk-SNARKs和以太坊来 隐私且trustlessly selling: 1)以太坊地址的私钥(ECDSA keypair)2)EdDSA签名3)Groth16 proof:借助递归性来匿名交易Groth16 proo…

RTK和CORS有什么区别?

高精度定位技术 关于高精度定位技术,RTK为业界熟知且被广泛应用,那么RTK到底是什么?仅仅是差分GPS吗? 其实并不尽然。 RTK RTK,载波相位差分技术,是实时处理两个测站载波相位观测量的差分方法&#xff0…

什么是OLAP

一、什么是OLAP OLAP(On-line Analytical Processing,联机分析处理)是在基于数据仓库多维模型的基础上实现的面向分析的各类操作的集合。可以比较下其与传统的OLTP(On-line Transaction Processing,联机事务处理&…

分布式集群框架——Google文件系统GFS

Google文件系统GFS Google文件系统(Google File System,GFS)是一个大型的分布式文件系统。它为Google云计算提供海量存储,并且与Chubby、MapReduce以及Bigtable等技术结合十分紧密,处于所有核心技术的底层。由于GFS并不…

TDesign表单rules通过函数 实现复杂逻辑验证输入内容

Element ui 中 我们可以通过validator 绑定函数来验证一些不在表单model中的值 又或者处理一下比较复杂的判断逻辑 TDesign也有validator 但比较直观的说 没有Element那么好用 这里 我们给validator绑定了我们自己的checkAge函数 这个函数中 只有一个参数 value 而且 如果你的…

ssm计算机网络课程试卷生成器系统源码

ssm计算机网络课程试卷生成器系统源码099 开发工具:idea 数据库mysql5.7 数据库链接工具:navcat,小海豚等 技术:ssm package com.controller;import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays;…

spring cloud、gradle、父子项目、微服务框架搭建---spring secuity oauth2、mysql 授权(九)

文章目录 一、二、授权服务2.1 初始化表结构2.2 引入依赖2.3 自定义 用户详情类 UserDetailsService2.4 授权配置 AuthorizationServerConfiguration2.5 Web安全配置 WebSecurityConfiguration2.6 默认生成接口 三、资源服务3.1 引入依赖3.2 资源服务 ResourceServerConfig 四、…

2023年DAMA-CDGA/CDGP数据治理认证线上班到这里

DAMA认证为数据管理专业人士提供职业目标晋升规划,彰显了职业发展里程碑及发展阶梯定义,帮助数据管理从业人士获得企业数字化转型战略下的必备职业能力,促进开展工作实践应用及实际问题解决,形成企业所需的新数字经济下的核心职业…