如何在Node.js中使用JWT对用户进行身份验证和授权
认证和授权是计算机安全的基本概念。您使用凭据(如用户名和密码)来证明您的身份并将自己标识为注册用户,然后获得额外的特权。
当您使用Facebook或Google帐户登录在线服务时,也适用此概念。
在本文中,我们将使用JWT(JSON Web Tokens)身份验证构建一个Nodejs API。本教程中要使用的工具有:
- Expressjs
- MongoDB数据库
- Mongoose
- Dotenv
- Bcryptjs
- Jsonwebtoken
认证与授权
什么是认证?
认证是通过获取凭据(如电子邮件、密码和令牌)来识别用户的过程。给定的凭据与注册用户的凭据进行比较,这些凭据可在本地计算机系统的文件或任何数据库中找到。如果给定的凭据与数据库中的可用数据匹配,认证过程就完成了,用户就被允许访问资源。
什么是授权?
授权发生在认证之后。每个授权都必须有一个认证过程。它是允许用户从系统或网站上访问资源的过程。在本教程中,我们将授权已登录用户访问用户数据。如果用户未登录,则无法访问数据。
授权的最佳例子是Facebook和Twitter等社交媒体平台。如果没有账户,您将无法访问社交媒体内容。
授权的另一个例子是基于订阅的内容,您可以通过登录网站进行认证,但在未订阅之前,您将无权访问内容。
先决条件
在继续之前,我假设您对Javascript和MongoDB有基本的了解,并且对Nodejs有很好的了解。
确保您已在本地计算机上安装了node和npm。要检查node和npm是否已安装在您的计算机上,请打开命令提示符并键入node -v和npm -v。结果应如下所示。
您的版本可能与我的不同。npm会随node一起自动下载。如果您还没有下载,请从NodeJS website下载。
您需要一个IDE(集成开发环境)来编写代码。在本教程中,我使用的是VS code editor。如果您有其他IDE,也可以使用。如果您的计算机上没有安装任何IDE,您可以从Visual Studio website下载。根据您的本地系统进行下载。
项目设置
在您的本地计算机上的任何位置创建一个名为nodeapi的文件夹,然后使用vs-code打开它。打开vs-code终端,然后通过键入以下内容来初始化node包管理器。
npm init -y
确保您在nodeapi目录下。
上述命令将创建一个package.json文件,其中包含我们在此项目中要使用的所有依赖项。
现在,我们将下载上面提到的所有软件包,现在在终端中键入并输入它们。
npm install express dotenv jsonwebtoken mongoose bcryptjs
现在,您将拥有如下所示的文件和文件夹。
创建服务器并连接数据库
现在创建一个名为index.js的文件和一个名为config的文件夹。在config文件夹内,创建两个名为conn.js和config.env的文件。分别写入以下代码。
index.js
const express = require('express');
const dotenv = require('dotenv');
// 使用其他库和文件配置dotenv文件
dotenv.config({path:'./config/config.env'});
// 从express中创建一个app
const app = express();
// 使用express.json获取json数据的请求
app.use(express.json());
// 监听服务器
app.listen(process.env.PORT,()=>{
console.log(`Server is listening at ${process.env.PORT}`);
})
如果您使用dotenv,则在调用使用环境变量的其他文件之前,在index.js文件中进行配置。
conn.js
const mongoose = require('mongoose');
mongoose.connect(process.env.URI,
{ useNewUrlParser: true,
useUnifiedTopology: true })
.then((data) => {
console.log(`Database connected to ${data.connection.host}`)
})
config.env
URI = 'mongodb+srv://ghulamrabbani883:[email protected]/?retryWrites=true&w=majority'
PORT = 5000
我使用的是mongo-DB Atlas URI,您也可以使用localhost。
创建模型和路由
模型是Mongo-DB数据库中数据的布局,将作为JSON文档存储。要创建模型,我们将使用mongoose schema。
路由是应用程序如何响应客户端请求的方式。我们将使用express router函数创建路由。
路由方法通常需要两个参数。第一个是路由,第二个是回调函数,用于定义当客户端请求时该路由应该做什么。
当需要时,它还接受第三个参数作为中间件函数,例如在身份验证过程中。由于我们正在构建经过身份验证的API,我们还将使用中间件函数来授权和验证用户。
现在我们将创建两个名为routes和models的文件夹。在routes文件夹内,创建一个名为userRoute.js的文件,在models文件夹内,创建一个名为userModel.js的文件。创建文件后,在各自的文件中写入以下代码。
userModel.js
const mongoose = require('mongoose');
// 使用mongoose创建模式
const userSchema = new mongoose.Schema({
name: {
type:String,
required:true,
minLength:[4,'名称应至少为4个字符']
},
email:{
type:String,
required:true,
unique:true,
},
password:{
type:String,
required:true,
minLength:[8,'密码应至少为8个字符']
},
token:{
type:String
}
})
// 创建模型
const userModel = mongoose.model('user',userSchema);
module.exports = userModel;
userRoute.js
const express = require('express');
// 创建express路由器
const route = express.Router();
// 导入userModel
const userModel = require('../models/userModel');
// 创建注册路由
route.post('/register',(req,res)=>{
})
// 创建登录路由
route.post('/login',(req,res)=>{
})
// 创建用户路由以获取用户数据
route.get('/user',(req,res)=>{
})
实现路由功能并创建JWT令牌
什么是JWT?
JSON web tokens (JWT) 是一个javascript库,用于创建和验证令牌。它是一个开放标准,用于在客户端和服务器之间共享信息。我们将使用JWT的两个函数。第一个函数是sign,用于创建新的令牌,第二个函数是verify,用于验证令牌。
什么是bcryptjs?
Bcryptjs是由Niels Provos和David Mazières创建的哈希函数。它使用哈希算法对密码进行哈希。在这个项目中,我们将使用两个最常见的bcryptjs函数。第一个bcryptjs函数是hash 用于生成哈希值,第二个函数是compare 函数用于比较密码。
实现路由功能
路由中的回调函数接受三个参数,request,response 和next函数。next参数是可选的,只有在需要时才传递。这些参数应按照请求、响应 和next 的顺序排列。现在使用以下代码修改userRoute.js,config.env和index.js文件。
userRoute.js
//Requiring all the necessary files and libraries
const express = require(‘express');
const bcrypt = require(‘bcryptjs');
const jwt = require(‘jsonwebtoken');
//Creating express router
const route = express.Router();
//Importing userModel
const userModel = require(‘../models/userModel');
//Creating register route
route.post(“/register”, async (req, res) => {
try {
const { name, email, password } = req.body;
//Check emptyness of the incoming data
if (!name || !email || !password) {
return res.json({ message: ‘请输入所有细节' })
}
//Check if the user already exist or not
const userExist = await userModel.findOne({ email: req.body.email });
if (userExist) {
return res.json({ message: ‘已存在使用给定电子邮件地址的用户' })
}
//Hash the password
const salt = await bcrypt.genSalt(10);
const hashPassword = await bcrypt.hash(req.body.password, salt);
req.body.password = hashPassword;
const user = new userModel(req.body);
await user.save();
const token = await jwt.sign({ id: user._id }, process.env.SECRET_KEY, {
expiresIn: process.env.JWT_EXPIRE,
});
return res.cookie({ ‘token': token }).json({ success: true, message: ‘用户注册成功', data: user })
} catch (error) {
return res.json({ error: error });
}
})
//Creating login routes
route.post(‘/login', async (req, res) => {
try {
const { email, password } = req.body;
//Check emptyness of the incoming data
if (!email || !password) {
return res.json({ message: ‘请输入所有细节' })
}
//Check if the user already exist or not
const userExist = await userModel.findOne({email:req.body.email});
if(!userExist){
return res.json({message:'凭证错误'})
}
//Check password match
const isPasswordMatched = await bcrypt.compare(password,userExist.password);
if(!isPasswordMatched){
return res.json({message:'凭证错误 pass'});
}
const token = await jwt.sign({ id: userExist._id }, process.env.SECRET_KEY, {
expiresIn: process.env.JWT_EXPIRE,
});
return res.cookie({“token”:token}).json({success:true,message:'成功登录'})
} catch (error) {
return res.json({ error: error });
}
})
//Creating user routes to fetch users data
route.get(‘/user', async (req, res) => {
try {
const user = await userModel.find();
if(!user){
return res.json({message:'未找到用户'})
}
return res.json({user:user})
} catch (error) {
return res.json({ error: error });
}
})
module.exports = route;
什么是中间件?
中间件是一个函数,它可以访问请求-响应周期中的请求、响应 对象和next函数。当函数执行完成时,将调用next函数。如上所述,当您必须执行另一个回调函数或中间件函数时,请使用next()。
现在创建一个名为middleware的文件夹,在其中创建名为auth.js的文件,并编写以下代码。
auth.js
const userModel = require('../models/userModel');
const jwt = require('jsonwebtoken');
const isAuthenticated = async (req,res,next)=>{
try {
const {token} = req.cookies;
if(!token){
return next('请登录以访问数据');
}
const verify = await jwt.verify(token,process.env.SECRET_KEY);
req.user = await userModel.findById(verify.id);
next();
} catch (error) {
return next(error);
}
}
module.exports = isAuthenticated;
现在安装cookie-parser库以配置cookieParser。cookieParser帮助您访问存储在cookie中的令牌。如果您的nodejs应用程序中没有配置cookieParser,则无法从请求对象的标头访问cookie。现在,在终端中输入以下内容来下载cookie-parser。
npm i cookie-parser
现在,您安装了cookieParser。通过修改index.js文件配置您的应用程序,并将中间件添加到“/user/”路由中。
index.js文件
const cookieParser = require('cookie-parser');
const express = require('express');
const dotenv = require('dotenv');
//使用任何其他库和文件配置dotenv文件
dotenv.config({path:'./config/config.env'});
require('./config/conn');
//从express创建一个应用程序
const app = express();
const route = require('./routes/userRoute');
//使用express.json来获取json数据的请求
app.use(express.json());
//配置cookie-parser
app.use(cookieParser());
//使用路由
app.use('/api', route);
//监听服务器
app.listen(process.env.PORT,()=>{
console.log(`服务器在${process.env.PORT}上监听`);
})
userRoute.js
//引入所有必要的文件和库
const express = require('express');
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');
const isAuthenticated = require('../middleware/auth');
//创建express路由器
const route = express.Router();
//导入userModel
const userModel = require('../models/userModel');
//创建用户路由以获取用户数据
route.get('/user', isAuthenticated, async (req, res) => {
try {
const user = await userModel.find();
if (!user) {
return res.json({ message: '找不到用户' })
}
return res.json({ user: user })
} catch (error) {
return res.json({ error: error });
}
})
module.exports = route;
只有当用户登录时才能访问“/user”路由。
在POSTMAN上检查API
在检查API之前,您需要修改package.json文件。添加以下代码行。
"scripts": {
"test": "echo "Error: no test specified" && exit 1",
"start": "node index.js",
"dev": "nodemon index.js"
},
您可以通过键入npm start来启动服务器,但这只会运行一次。为了在更改文件时保持服务器运行,您将需要nodemon。通过在终端中输入以下内容来下载它
npm install -g nodemon
-g标志将在您的本地系统上全局下载nodemon。您无需为每个新项目再次下载它。
要运行服务器,请在终端中输入npm run dev。您将获得以下结果。
最后,您的代码已完成,并且服务器正常运行,请转到postman并检查是否正常工作。
什么是POSTMAN?
POSTMAN是一种用于设计、构建、开发和测试API的软件工具。
如果您尚未在计算机上下载postman,请从postman website下载。
现在打开postman,并创建一个名为nodeAPItest的集合,在其中创建三个请求:register,login和user。您应该有以下文件。
当您将JSON数据发送到“localhost:5000/api/register”时,您将获得以下结果。
由于我们在注册过程中创建并保存令牌到cookie中,因此当您请求“localhost:5000/api/user”路由时,您可以获得用户详细信息。您可以在POSTMAN上检查其余请求。
如果您想获取完整的代码,可以从我的github account获取。
结论
在本教程中,我们学习了如何使用JWT令牌对NodeJS API进行身份验证。我们还授权用户访问用户数据。
编码愉快!