Node.js开发进阶:9个步骤实现高级JWT身份验证
JSON Web 令牌(JWT)被广泛使用于 Web 应用程序中,用于实现身份验证。作为 Web 开发的重要组成部分,身份验证在确保应用程序的安全性和可靠性方面具有关键作用。由于其简单性、安全性和可扩展性,JWT 成为了一种流行的身份验证方法。
本文将指导您在 Node.js 应用程序中使用 MongoDB 进行数据存储,以实现 JWT 身份验证的过程。
与传统的身份验证方法相比,JWT 身份验证具有许多优势。JWT 是基于 JSON 结构的令牌,包含了必要的身份验证信息,并通过数字签名保证了其可靠性。此外,JWT 令牌的简洁性使得它在网络传输中的效率更高。
在 Node.js 应用程序中实现 JWT 身份验证的过程相对简单。首先,您需要使用适当的包或模块来处理 JWT 令牌的创建和验证。然后,您需要将用户的身份信息存储到 MongoDB 数据库中,以便后续进行验证。
通过使用 MongoDB 进行数据存储,您可以灵活地管理用户信息,并实现高效的身份验证功能。将用户身份信息存储到数据库中,可以方便地进行查询和更新操作,同时保证数据的安全性。
总的来说,JWT 身份验证在 Node.js 应用程序中的实现是一个重要且值得探索的话题。本文对使用 MongoDB 进行数据存储的 JWT 身份验证方法进行了详细讲解,希望能够帮助读者更好地理解和应用这一技术。
在开始之前,我假设你已经安装了 Node.js、MongoDB 和 VS Code,并且你知道如何创建 MongoDB 数据库和基本的 RESTful API。
什么是 JWT 认证?
JWT 身份验证依赖于 JSON Web 令牌来确认 Web 应用中用户的身份。JSON Web 令牌是使用密钥对进行数字签名的编码 JSON 对象。
简而言之,JWT 身份验证就像为网站提供一个密码。一旦你登录成功,你就得到了这个密码。
JSON Web Token
由三部分组成,由点(.)分隔:
- Header
- Payload
- Signature
以下是 JWT 的基本结构:
xxxx.yyyy.zzzz
- Header:这部分包含有关令牌的信息,如其类型和如何保护。
- Payload:这部分包含关于用户的声明,如用户名或角色。
- Signature:确保令牌的完整性,并验证它没有被更改,这可以确保代码安全,不会被篡改。
当你登录成功时,你会得到这个代码。每次你想访问某个数据时,你都要携带这个代码来证明是你。系统会检查代码是否有效,然后让你获取数据!
接下来让我们看看在 node.js 项目中进行 JWT 身份验证的步骤。
步骤 1:新建项目
首先为您的项目创建一个新目录,并使用以下命令进入到该目录。
mkdir nodejs-jwt-auth cd nodejs-jwt-auth
通过在终端中运行以下命令初始化项目(确保您位于新创建的项目文件夹中)。
npm init -y
接下来通过以下命令安装必要的依赖项:
npm install express mongoose jsonwebtoken dotenv
上面的命令将安装:
- express: 用于构建 Web 服务器。
- mongoose:MongoDB 的数据库。
- jsonwebtoken:用于生成和验证 JSON Web 令牌(JWT)以进行身份验证。
- dotenv:用于从.env 文件加载环境变量。
现在您的package.json
文件应该看起来像这样:
步骤 2:连接 MongoDB 数据库
要连接 MongoDB 数据库,请查看以下链接中的具体操作流程。
https://shefali.dev/restful-api/#Step_4_Creating_a_MongoDB_Database
步骤 3:创建 .env 文件
为了 MongoDB 连接地址的安全,让我们在根目录下创建一个名为 .env
的新文件。
将以下代码添加到.env
文件中。
MONGODB_URL=<Your MongoDB Connection String> SECRET_KEY="your_secret_key_here"
将<Your MongoDB Connection String>
替换为您从 MongoDB Atlas 获得的连接字符串(在步骤 2 中),并将your_secret_key_here
替换为您想要的密钥字符串。现在你的.env
文件应该是这样的。
MONGODB_URL='mongodb+srv://shefali:********@cluster0.sscvg.mongodb.net/nodejs-jwt-auth' SECRET_KEY="ThisIsMySecretKey"
在MONGODB_URL
最后我们加入node.js-jwt-auth
,这是我们的数据库名称。
步骤 4:Express
在根目录下创建一个名为index.js
的文件,并将以下代码添加到该文件中。
const express = require("express"); const mongoose = require("mongoose"); require("dotenv").config(); //for using variables from .env file. const app = express(); const port = 3000; //middleware provided by Express to parse incoming JSON requests. app.use(express.json()); mongoose.connect(process.env.MONGODB_URL).then(() => { console.log("MongoDB is connected!"); }); app.get("/", (req, res) => { res.send("Hello World!"); }); app.listen(port, () => { console.log(`Server is listening on port ${port}`); });
现在我们可以通过以下命令运行服务器。
node index.js
输出应如下图所示:
通过使用命令node index.js
,您必须在每次更改文件时重新启动服务器。为了避免这种情况,您可以使用以下命令安装nodemon
。
npm install -g nodemon
现在使用下面的命令运行服务器,它会在每次更改文件时自动重新启动服务器。
nodemon index.js
步骤 5:创建用户数据库模型
在根目录下创建一个名为models
的新目录,并在其中创建一个名为User.js
的新文件。
现在让我们为我们的项目创建一个简单的模型,将以下代码添加到User.js
文件中。
const mongoose = require("mongoose"); const userSchema = new mongoose.Schema({ username: { type: String, required: true, unique: true, }, password: { type: String, required: true, }, }); module.exports = mongoose.model("User", userSchema);
步骤 6:实现身份验证路由
在根目录中,创建一个名为routes
的新目录,并在其中创建一个名为auth.js
的文件。
然后将以下代码添加到该文件中:
const express = require("express"); const jwt = require("jsonwebtoken"); const User = require("../models/User"); const router = express.Router(); // Signup route router.post("/signup", async (req, res) => { try { const { username, password } = req.body; const user = new User({ username, password }); await user.save(); res.status(201).json({ message: "New user registered successfully" }); } catch (error) { res.status(500).json({ message: "Internal server error" }); } }); // Login route router.post("/login", async (req, res) => { const { username, password } = req.body; try { const user = await User.findOne({ username }); if (!user) { return res.status(401).json({ message: "Invalid username or password" }); } if (user.password !== password) { return res.status(401).json({ message: 'Invalid username or password' }); } // Generate JWT token const token = jwt.sign( { id: user._id, username: user.username }, process.env.SECRET_KEY ); res.json({ token }); } catch (error) { res.status(500).json({ message: "Internal server error" }); } }); module.exports = router;
分解上面的代码
导入依赖:
const express = require("express"); const jwt = require("jsonwebtoken"); const User = require("../models/User"); const router = express.Router();
在这里,我们导入以下依赖项:
- express: 用于构建 Web 服务器。
- jsonwebtoken:用于生成和验证 JSON Web 令牌(JWT)以进行身份验证。
- User:从第 5 步中创建的 User 模块导入的模型。
- router:
Express
中的Router()
函数用于单独定义路由,然后将其合并到主应用程序中。
注册路由:
// Signup route router.post("/signup", async (req, res) => { try { const { username, password } = req.body; const user = new User({ username, password }); await user.save(); res.status(201).json({ message: "New user registered successfully" }); } catch (error) { res.status(500).json({ message: "Internal server error" }); } });
- 此路由监听对
/signup
的 POST 请求。 - 当接收到请求时,它从请求体中提取
username
和password
。 - 然后使用提供的用户名和密码创建
User
模型的一个新实例。 - 调用
save()
方法将新用户保存到数据库。 - 如果用户成功保存,它会返回一个状态码 201 和一个 JSON 消息,表示“新用户注册成功”。
- 如果在此过程中发生错误,它会捕获错误并以状态代码 500 和错误消息“内部服务器错误”进行响应。
登录路由:
// Login route router.post("/login", async (req, res) => { const { username, password } = req.body; try { const user = await User.findOne({ username }); if (!user) { return res.status(401).json({ message: "Invalid username or password" }); } if (user.password !== password) { return res.status(401).json({ message: 'Invalid username or password' }); } // Generate JWT token const token = jwt.sign( { id: user._id, username: user.username }, process.env.SECRET_KEY ); res.json({ token }); } catch (error) { res.status(500).json({ message: "Internal server error" }); } });
- 此路由监听对
/login
的POST
请求。 - 当接收到请求时,它从请求体中提取
username
和password
。 - 然后在数据库中使用提供的
username
搜索用户。 - 如果没有找到用户,它会返回一个状态码 401(未经授权)和一个 JSON 消息,指示用户名或密码无效。
- 如果找到用户,它会检查提供的
password
是否与数据库中存储的密码匹配。 - 如果密码不匹配,它会返回一个状态码 401(未经授权)和一个 JSON 消息,指示用户名或密码无效。
- 如果密码匹配,它将使用
jwt.sign()
生成一个 JWT。 - 生成的令牌然后作为 JSON 响应发送。
- 如果在此过程中出现错误,它会捕获错误并以状态代码 500 和错误消息“内部服务器错误”进行响应。
最后路由被导出以在index.js
文件中使用。
module.exports = router;
步骤 7:使用中间件保护路由
在根目录中,创建一个名为middleware.js
的新文件,并将以下代码添加到该文件中。
const jwt = require("jsonwebtoken"); function verifyJWT(req, res, next) { const token = req.headers["authorization"]; if (!token) { return res.status(401).json({ message: "Access denied" }); } jwt.verify(token, process.env.SECRET_KEY, (err, data) => { if (err) { return res.status(401).json({ message: "Failed to authenticate token" }); } req.user = data; next(); }); } module.exports = verifyJWT;
此代码是一个中间件函数,用于在应用程序中验证 JSON Web 令牌(JWT)。
分解上面的代码:
- 在第一行中,我们导入
jsonwebtoken
库。 - 然后定义
verifyJWT
中间件函数,它有三个参数:req
(请求对象)、res
(响应对象)和next
(下一个中间件函数)。 - 在中间件函数内部,它首先从请求头中提取 token 令牌。
- 如果请求头中没有令牌,它将返回 401(未经授权)状态沿着 JSON 响应,指示“拒绝访问”。
- 如果存在令牌,它会尝试使用
jwt.verify()
进行验证。如果验证失败,它会捕获错误并返回一个 401 状态,其中包含一个 JSON 响应,指示“Failed to authenticate token”。 - 如果令牌被成功验证,它将解码的令牌数据附加到
req.user
对象。 - 最后导出
verifyJWT
函数,以便它可以用作应用程序其他部分的中间件。
第 8 步:验证 JWT
现在要验证 JWT,请修改index.js
,如下所示:
const express = require('express'); const authRouter = require('./routes/auth'); const mongoose = require("mongoose"); const verifyJWT = require("./middleware") require("dotenv").config(); //for using variables from .env file. const app = express(); const PORT = 3000; mongoose.connect(process.env.MONGODB_URL).then(() => { console.log("MongoDB is connected!"); }); app.use(express.json()); //Authentication route app.use('/auth', authRouter); //decodeDetails Route app.get('/decodeDetails', verifyJWT, (req, res) => { const { username } = req.user; res.json({ username }); }); app.listen(PORT, () => { console.log(`Server is running on port ${PORT}`); });
在上面的代码中,/auth
路由是authRouter
处理,其中包含的终端用户认证,例如登录和注册。
app.get('/decodeDetails', verifyJWT, (req, res) => { const { username } = req.user; res.json({ username }); });
- 当向
/decodeDetails
发出请求时,verifyJWT
中间件验证附加到请求的JWT
令牌。 - 如果令牌有效,则中间件从
req.user
中存储的解码令牌数据中提取username
。 - 最后路由处理程序发送一个
JSON
响应,其中包含从令牌中提取的username
。
步骤 9:测试 API
注册
向http://localhost:3000/auth/signup
发送一个 POST 请求,其中包含Headers Content-Type : application/json
和以下JSON
主体:
{ "username": "shefali", "password": "12345678" }
在响应中,您将看到消息“新用户注册成功”。
登录
向http://localhost:3000/auth/login
发送一个 POST 请求,其中包含Header Content-Type : application/json
和JSON
主体以及用户名和密码,这是您在注册路由中创建的。
{ "username": "shefali", "password": "12345678" }
在响应中,您将收到一个令牌。记下这个令牌,因为在测试decodeDetails
路由时需要它。
decodeDetails
向http://localhost:3000/decodeDetails
发送一个 GET 请求,并带有一个带有令牌值的Authorization
头(您在测试登录路由时得到了它)。
在响应中,您将获得用户名。恭喜你!
您已经在 Node.js 应用程序中成功实现了JWT
身份验证。这种方法提供了一种安全有效的方式来验证 Web 应用程序中的用户。
码云笔记 » Node.js开发进阶:9个步骤实现高级JWT身份验证