如何在Node.js中使用JWT令牌构建认证API
在本教程中,我们将学习如何使用 JWT在 Node.js来保证端点的安全,甚至是对用户进行认证。
编写代码和开发应用程序是非常简单的。然而,我们如何处理认证,以及最可能的是授权?
什么是认证和授权
认证和授权被用于安全领域,特别是在涉及到获得系统的访问时。然而,进入一个房子(认证)和你在里面可以做什么(授权)之间有很大的区别。
认证
认证是通过获取凭证来验证用户的身份,并使用这些凭证来确认用户的身份的过程。如果凭证是合法的,则授权过程开始。授权过程总是遵循认证程序。
你已经知道了认证过程,因为我们每天都在做,无论是在工作中(登录你的电脑)还是在家里(登录一个网站)。然而,事实是,大多数连接到互联网的 "东西 "都需要你通过提供凭证来证明你的身份。
授权
授权是通过确定已认证的用户是否有系统访问权限来允许他们访问资源的过程。通过给予或拒绝已认证用户的特定许可,授权使你能够控制访问权限。
所以,授权是在系统验证了你的身份后发生的,授予你对信息、文件、数据库、资金、场所等资源的完全访问权。也就是说,授权会影响你访问系统的能力以及你可以访问的程度。
什么是JWT
JSON网络令牌(JWT)是一个RFC 7519开放的行业标准,用于表示双方的索赔。例如,你可以使用jwt.io来解码、验证和生产JWT。
JWT规定了一种紧凑和独立的方法,在双方之间以JSON对象的形式交流信息。因为它是经过签名的,所以这些信息可以被检查和信任。JWT可以使用秘密(使用HMAC算法)或RSA或ECDSA公共/私人密钥组合进行签名。稍后,我们将看到一些如何使用它们的例子。
前提条件
要跟上本教程,你将需要。
- 具有JavaScript的工作知识。
- 对Node.js有良好的理解。
- 对MongoDB或你选择的任何数据库有基本了解。
- Postman和一些关于如何使用Postman的知识。
在Node.js中使用JWT令牌进行认证的API开发
为了开始,我们需要设置我们的项目。
打开Visual Studio Code,在你的机器上导航到一个你选择的目录,并在终端上打开它。
然后执行。
code.
注意:如果你的系统上没有安装Visual Studio Code,
code .,就不会工作。
第1步 - 创建一个目录并初始化npm
通过输入以下命令创建一个目录并初始化npm 。
- Windows power shell
mkdir jwt-project
cd jwt-project
npm init -y
- Linux
mkdir jwt-project
cd jwt-project
npm init -y
第2步 - 创建文件和目录
在第1步中,我们用命令npm init -y 来初始化npm,它自动创建了一个package.json。
我们需要使用下面的命令创建model,middleware,config 目录和它们的文件,例如user.js,auth.js,database.js 。
mkdir model middleware config
touch config/database.js middleware/auth.js model/user.js
现在我们可以用命令在我们项目的根目录下创建index.js 和app.js 文件。
touch app.js index.js
如下面的图片所示。

第3步 - 安装依赖项
我们将安装几个依赖项,如mongoose,jsonwebtoken,express dotenv bcryptjs 和开发依赖项,如nodemon ,以便在我们自动进行修改时重新启动服务器。
我们将安装mongoose,因为我将在本教程中使用MongoDB。
我们将根据我们数据库中的内容来验证用户的证书。所以整个验证过程并不局限于我们在本文中使用的数据库。
npm install mongoose express jsonwebtoken dotenv bcryptjs
npm install nodemon -D
第4步 - 创建一个Node.js服务器并连接你的数据库
现在,让我们创建我们的Node.js服务器,并通过添加以下代码段来连接我们的数据库app.js,index.js , database.js .env 依次进行。
在我们的database.js.
config/database.js:
const mongoose = require("mongoose");
const { MONGO_URI } = process.env;
exports.connect = () => {
// Connecting to the database
mongoose
.connect(MONGO_URI, {
useNewUrlParser: true,
useUnifiedTopology: true,
useCreateIndex: true,
useFindAndModify: false,
})
.then(() => {
console.log("Successfully connected to database");
})
.catch((error) => {
console.log("database connection failed. exiting now...");
console.error(error);
process.exit(1);
});
};
在我们的app.js 。
jwt-project/app.js
require("dotenv").config();
require("./config/database").connect();
const express = require("express");
const app = express();
app.use(express.json());
// Logic goes here
module.exports = app;
在我们的index.js 。
jwt-project/index.js
const http = require("http");
const app = require("./app");
const server = http.createServer(app);
const { API_PORT } = process.env;
const port = process.env.PORT || API_PORT;
// server listening
server.listen(port, () => {
console.log(`Server running on port ${port}`);
});
如果你注意到,我们的文件需要一些环境变量。如果你还没有,你可以创建一个新的.env 文件,并在启动我们的应用程序之前添加你的变量。
在我们的.env.
API_PORT=4001
MONGO_URI= //Your database URI here
要启动我们的服务器,编辑我们的package.json 中的scripts对象,使其看起来像下图所示。
"scripts": {
"start": "node index.js",
"dev": "nodemon index.js",
"test": "echo \"Error: no test specified\" && exit 1"
}
上面的片段已经成功地插入到app.js ,index.js ,和database.js 。首先,我们在index.js 中建立了我们的node.js服务器,并导入了配置有路由的app.js文件。
然后,如database.js 中所示,我们使用mongoose 来创建一个与我们的数据库的连接。
执行命令npm run dev 。
服务器和数据库都应该启动并运行,不会崩溃。
第5步 - 创建用户模型和路由
我们将为首次注册时的用户细节定义我们的模式,并在登录时根据保存的凭证进行验证。
在model 文件夹内的user.js ,添加以下片段。
model/user.js
const mongoose = require("mongoose");
const userSchema = new mongoose.Schema({
first_name: { type: String, default: null },
last_name: { type: String, default: null },
email: { type: String, unique: true },
password: { type: String },
token: { type: String },
});
module.exports = mongoose.model("user", userSchema);
现在让我们分别为register 和login 创建路由。
在根目录下的app.js ,添加以下代码段,用于注册和登录。
app.js
// importing user context
const User = require("./model/user");
// Register
app.post("/register", (req, res) => {
// our register logic goes here...
});
// Login
app.post("/login", (req, res) => {
// our login logic goes here
});
第6步 - 实现注册和登录功能
我们将在我们的应用程序中实现这两个路由。我们将使用JWT来签署证书,并使用bycrypt 来加密密码,然后再将它们存储在我们的数据库中。
从/register 路由,我们将。
- 获取用户输入。
- 验证用户输入。
- 验证该用户是否已经存在。
- 加密用户密码。
- 在我们的数据库中创建一个用户。
- 最后,创建一个签名的JWT令牌。
修改我们先前创建的/register 路由结构,使其看起来如下所示。
app.js
// ...
app.post("/register", async (req, res) => {
// Our register logic starts here
try {
// Get user input
const { first_name, last_name, email, password } = req.body;
// Validate user input
if (!(email && password && first_name && last_name)) {
res.status(400).send("All input is required");
}
// check if user already exist
// Validate if user exist in our database
const oldUser = await User.findOne({ email });
if (oldUser) {
return res.status(409).send("User Already Exist. Please Login");
}
//Encrypt user password
encryptedPassword = await bcrypt.hash(password, 10);
// Create user in our database
const user = await User.create({
first_name,
last_name,
email: email.toLowerCase(), // sanitize: convert email to lowercase
password: encryptedPassword,
});
// Create token
const token = jwt.sign(
{ user_id: user._id, email },
process.env.TOKEN_KEY,
{
expiresIn: "2h",
}
);
// save user token
user.token = token;
// return new user
res.status(201).json(user);
} catch (err) {
console.log(err);
}
// Our register logic ends here
});
// ...
注意:用一个
TOKEN_KEY来更新你的.env文件,它可以是一个随机字符串。
使用Postman来测试端点,在成功注册后,我们会得到如下所示的响应。

对于/login 路线,我们将。
- 获取用户输入。
- 验证用户输入。
- 验证用户是否存在。
- 根据我们先前保存在数据库中的密码来验证用户密码。
- 最后,创建一个签名的JWT令牌。
修改我们先前创建的/login 路由结构,使其看起来像下面所示。
// ...
app.post("/login", async (req, res) => {
// Our login logic starts here
try {
// Get user input
const { email, password } = req.body;
// Validate user input
if (!(email && password)) {
res.status(400).send("All input is required");
}
// Validate if user exist in our database
const user = await User.findOne({ email });
if (user && (await bcrypt.compare(password, user.password))) {
// Create token
const token = jwt.sign(
{ user_id: user._id, email },
process.env.TOKEN_KEY,
{
expiresIn: "2h",
}
);
// save user token
user.token = token;
// user
res.status(200).json(user);
}
res.status(400).send("Invalid Credentials");
} catch (err) {
console.log(err);
}
// Our register logic ends here
});
// ...
使用Postman进行测试,我们会在成功登录后得到如下的响应。

第7步 - 创建认证的中间件
我们可以成功创建并登录一个用户。不过,我们还是要创建一个路由,在头中需要一个用户令牌,也就是我们之前生成的JWT令牌。
在auth.js 内添加以下代码段。
middleware/auth.js
const jwt = require("jsonwebtoken");
const config = process.env;
const verifyToken = (req, res, next) => {
const token =
req.body.token || req.query.token || req.headers["x-access-token"];
if (!token) {
return res.status(403).send("A token is required for authentication");
}
try {
const decoded = jwt.verify(token, config.TOKEN_KEY);
req.user = decoded;
} catch (err) {
return res.status(401).send("Invalid Token");
}
return next();
};
module.exports = verifyToken;
现在让我们创建/welcome 路由,并用以下代码段更新app.js ,以测试中间件。
app.js
const auth = require("./middleware/auth");
app.post("/welcome", auth, (req, res) => {
res.status(200).send("Welcome 🙌 ");
});
当我们试图访问我们刚刚创建的/welcome 路由时,看到下面的结果,没有在头中传递一个带有x-access-token 密钥的令牌。

我们现在可以在头中添加一个token ,键为x-access-token ,然后重新测试。
请看下面的图片,以了解响应情况。

你可以点击这里查看GitHub上的完整代码。
总结
在本教程中,我们了解了JWT、认证、授权以及如何在Node.js中使用JWT令牌开发API进行认证。