【Node.js从基础到高级运用】十二、身份验证与授权:JWT

身份验证与授权是现代Web应用中不可或缺的部分。了解如何在Node.js应用中实施这些机制,将使你能够构建更安全、更可靠的应用程序。本文将引导你通过使用JWT实现用户注册、登录和权限控制的过程。

JWT(Json Web Token)

JWT是一种用于双方之间安全地传输信息的简洁的、URL安全的表示声明的方法。它由三部分组成:头部(Header)、载荷(Payload)、签名(Signature)。

JWT的实现步骤

安装依赖

首先,安装JWT相关的npm包:

bash 复制代码
npm install jsonwebtoken --save

创建JWT

在用户登录成功后,你需要创建一个token发送给用户:

javascript 复制代码
const jwt = require('jsonwebtoken');

const user = { id: user.id }; // 用户的唯一标识
const secret = 'your_secret_key'; // 保持安全的秘钥
const token = jwt.sign(user, secret, { expiresIn: '1h' }); // 有效期1小时

验证JWT

创建一个中间件来验证每次请求的JWT:

javascript 复制代码
function authenticateToken(req, res, next) {
    const authHeader = req.headers['authorization'];
    // 从请求头中获取'authorization'字段
    const token = authHeader && authHeader.split(' ')[1];
    if (token == null) return res.sendStatus(401);
    // 如果没有token,则返回401

    jwt.verify(token, 'your_secret_key', (err, user) => {
        if (err) return res.sendStatus(403);
        // 如果token验证失败,则返回403
        req.user = user;
        next();
    });
}

权限控制

使用前面创建的authenticateToken中间件来控制访问特定路由的权限:

javascript 复制代码
app.get('/protected', authenticateToken, (req, res) => {
    res.json({ message: 'This is protected' });
    // 这个路由现在受到保护,只有带有有效JWT的请求才能访问
});

结合Express + MongoDB + JWT 示例

准备工作

安装必要的npm包

bash 复制代码
npm install express [email protected] jsonwebtoken bcryptjs body-parser --save

这些包包括Express框架、Mongoose(MongoDB的ODM)、jsonwebtoken(用于JWT操作)、bcryptjs(用于密码加密)和body-parser(用于解析请求体)。

启动MongoDB服务

确保MongoDB服务在本地运行或者你有一个MongoDB Atlas云服务的实例。

bash 复制代码
mongo

开始

设置Express应用
javascript 复制代码
const express = require('express'); // 引入express模块
const bodyParser = require('body-parser'); // 引入body-parser模块用于解析请求体
const mongoose = require('mongoose'); // 引入mongoose模块连接MongoDB
const { register, login, authenticateToken } = require('./controllers/authController'); // 引入控制器

const app = express(); // 创建express应用
const PORT = process.env.PORT || 3000; // 定义端口号

app.use(bodyParser.json()); // 使用body-parser中间件解析JSON格式请求体
// 构建MongoDB连接的URL
const url = 'mongodb://localhost:27017/mydb'; // 这里将地址和数据库名拼接在了一起
// 连接到MongoDB数据库
//mongoose.connect方法用于初始化数据库连接。
//它接受两个参数:
//第一个参数是MongoDB的连接字符串,
//第二个参数是一个选项对象,
//这里使用了useNewUrlParser和useUnifiedTopology选项
//以使用新的URL解析器和驱动引擎,这两个选项有助于避免一些常见的连接警告。
mongoose.connect(url, { useNewUrlParser: true, useUnifiedTopology: true })
  .then(() => console.log('MongoDB connected')) // 连接成功后打印消息
  .catch(err => console.log(err)); // 连接失败打印错误信息

// 注册和登录路由
app.post('/register', register);
app.post('/login', login);

// 受保护的路由示例,使用authenticateToken中间件保护
app.get('/protected', authenticateToken, (req, res) => {
  res.json({ message: 'This is protected' }); // 受保护的资源
});

// 启动服务器
app.listen(PORT, () => {
  console.log(`Server is running on port ${PORT}`);
});
创建User模型

models/User.js中:

javascript 复制代码
const mongoose = require('mongoose'); // 引入mongoose模块

// 定义用户模型的schema
const userSchema = new mongoose.Schema({
  username: { type: String, required: true, unique: true }, // 用户名,必填,唯一
  password: { type: String, required: true } // 密码,必填
});

// 导出模型
module.exports = mongoose.model('User', userSchema);
实现注册与登录逻辑

controllers/authController.js中:

javascript 复制代码
const bcrypt = require('bcryptjs'); // 引入bcryptjs用于密码加密
const jwt = require('jsonwebtoken'); // 引入jsonwebtoken用于生成JWT
const User = require('../models/User'); // 引入User模型

// 注册逻辑
exports.register = async (req, res) => {
  const { username, password } = req.body; // 从请求体获取用户名和密码
  const hashedPassword = await bcrypt.hash(password, 10); // 对密码进行加密

  try {
    // 创建新用户并保存到数据库
    const newUser = await User.create({ username, password: hashedPassword });
    res.status(201).json(newUser); // 发送201响应和新用户信息
  } catch (error) {
    res.status(500).json({ error: error.message }); // 发送500响应和错误信息
  }
};

// 登录逻辑
exports.login = async (req, res) => {
  const { username, password } = req.body; // 从请求体获取用户名和密码
  const user = await User.findOne({ username }); // 查找用户

  // 如果用户不存在或密码错误
  if (!user || !(await bcrypt.compare(password, user.password))) {
    return res.status(401).send('Invalid credentials'); // 发送401响应
  }

  // 生成JWT
  const token = jwt.sign({ userId: user._id }, 'your_secret_key', { expiresIn: '1h' });
  res.json({ token }); // 发送包含JWT的响应
};

// JWT验证中间件
exports.authenticateToken = (req, res, next) => {
  const authHeader = req.headers['authorization']; // 获取请求头中的authorization字段
  const token = authHeader && authHeader.split(' ')[1]; // 提取token
  if (!token) return res.sendStatus(401); // 如果没有token,发送401响应

  // 验证token
  jwt.verify(token, 'your_secret_key', (err, decoded) => {
    if (err) return res.sendStatus(403); // 如果验证失败,发送403响应
    req.userId = decoded.userId; // 将解码的用户ID添加到请求对象
    next(); // 调用下一个中间件
  });
};
测试
  1. 使用Postman或任何API测试工具,先调用/register端点注册新用户。
  2. 使用注册信息调用/login端点,你会收到一个JWT。
  3. 尝试访问/protected端点,把JWT添加到请求头中(通常是Authorization: Bearer <your_token>)。
  4. 如果一切配置正确,你应该能够访问受保护的路由。

http://localhost:3000/register

http://localhost:3000/login

未加token,访问http://localhost:3000/protected

加token,访问http://localhost:3000/protected

总结

本文介绍了如何在Node.js应用中实现用户身份验证和授权,特别是通过使用Node.js、Express和MongoDB实现JWT基于的身份验证和授权的全面指导,包括注册、登录和访问受保护资源的流程。这为构建安全的Web应用程序奠定了坚实的基础。

相关推荐
橘右溪37 分钟前
Node.js核心模块及Api详解
node.js
在下千玦19 小时前
#管理Node.js的多个版本
node.js
你的人类朋友19 小时前
MQTT协议是用来做什么的?此协议常用的概念有哪些?
javascript·后端·node.js
还是鼠鼠1 天前
Node.js中间件的5个注意事项
javascript·vscode·中间件·node.js·json·express
南通DXZ1 天前
Win7下安装高版本node.js 16.3.0 以及webpack插件的构建
前端·webpack·node.js
你的人类朋友1 天前
浅谈Object.prototype.hasOwnProperty.call(a, b)
javascript·后端·node.js
前端太佬1 天前
暂时性死区(Temporal Dead Zone, TDZ)
前端·javascript·node.js
Mintopia1 天前
Node.js 中 http.createServer API 详解
前端·javascript·node.js
你的人类朋友1 天前
CommonJS模块化规范
javascript·后端·node.js
Mintopia2 天前
Node.js 中 fs.readFile API 的使用详解
前端·javascript·node.js