当前位置: 首页 > news >正文

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

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

JWT(Json Web Token)

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

JWT的实现步骤

安装依赖

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

npm install jsonwebtoken --save

创建JWT

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

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:

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

权限控制

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

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

结合Express + MongoDB + JWT 示例

准备工作

安装必要的npm包

npm install express mongoose@4.4.0 jsonwebtoken bcryptjs body-parser --save

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

启动MongoDB服务

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

mongo

开始

设置Express应用
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中:

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中:

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响应}// 生成JWTconst 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]; // 提取tokenif (!token) return res.sendStatus(401); // 如果没有token,发送401响应// 验证tokenjwt.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应用程序奠定了坚实的基础。

相关文章:

  • 操作系统(多线程)
  • 基于单片机的车载酒精含量自检系统设计与实现
  • Selenium 学习(0.20)——软件测试之单元测试
  • 综合知识篇02-UML统一建模语言(2024年软考高级系统架构设计师冲刺知识点总结系列文章)
  • ChatGPT-Next-Web SSRF漏洞+XSS漏洞复现(CVE-2023-49785)
  • UE4案例记录
  • 二 centos 7.9 磁盘挂载
  • Unreal发布Android在刘海屏手机上不能全屏显示问题
  • CompletableFuture原理与实践-外卖商家端API的异步化
  • 爬虫3_爬取翻页URL不变的网站
  • ETAS工程在ISOLA软件中如何看到OS中断的配置
  • bootstrap企业网站前端模板
  • COX回归影响因素分析的基本过程与方法
  • Jmeter——循环控制器中实现Counter计数器的次数重置
  • C/C++动态链接库的封装和调用
  • $translatePartialLoader加载失败及解决方式
  • @jsonView过滤属性
  • CSS 专业技巧
  • CSS3 变换
  • C语言笔记(第一章:C语言编程)
  • HTTP中的ETag在移动客户端的应用
  • JS创建对象模式及其对象原型链探究(一):Object模式
  • leetcode-27. Remove Element
  • QQ浏览器x5内核的兼容性问题
  • vue脚手架vue-cli
  • 从setTimeout-setInterval看JS线程
  • 分布式熔断降级平台aegis
  • 观察者模式实现非直接耦合
  • 机器人定位导航技术 激光SLAM与视觉SLAM谁更胜一筹?
  • 简单基于spring的redis配置(单机和集群模式)
  • 聊聊hikari连接池的leakDetectionThreshold
  • 收藏好这篇,别再只说“数据劫持”了
  • 思考 CSS 架构
  • 微信小程序设置上一页数据
  • 限制Java线程池运行线程以及等待线程数量的策略
  • 鱼骨图 - 如何绘制?
  • 与 ConTeXt MkIV 官方文档的接驳
  • d²y/dx²; 偏导数问题 请问f1 f2是什么意思
  • media数据库操作,可以进行增删改查,实现回收站,隐私照片功能 SharedPreferences存储地址:
  • ​二进制运算符:(与运算)、|(或运算)、~(取反运算)、^(异或运算)、位移运算符​
  • ## 临床数据 两两比较 加显著性boxplot加显著性
  • #我与Java虚拟机的故事#连载12:一本书带我深入Java领域
  • (2)nginx 安装、启停
  • (DenseNet)Densely Connected Convolutional Networks--Gao Huang
  • (Redis使用系列) Springboot 在redis中使用BloomFilter布隆过滤器机制 六
  • (附源码)spring boot建达集团公司平台 毕业设计 141538
  • (蓝桥杯每日一题)平方末尾及补充(常用的字符串函数功能)
  • (七)c52学习之旅-中断
  • (太强大了) - Linux 性能监控、测试、优化工具
  • (转载)跟我一起学习VIM - The Life Changing Editor
  • .Net 6.0 处理跨域的方式
  • .NET Compact Framework 多线程环境下的UI异步刷新
  • .NET构架之我见
  • .NET轻量级ORM组件Dapper葵花宝典
  • /proc/stat文件详解(翻译)