Express的使用笔记10 给登录接口添加返回token与其它接口进行token校验处理

news/2024/12/17 14:29:49/文章来源:https://www.cnblogs.com/jocelyn11/p/18610832

按照常规,用户登录成功的时候是会返回一个token值,前端就可以将这个token存储到cookie中随后在其他接口使用的时候放置在Headers中进行传递。

实现这个功能,首先需要了解JWT Secret(密钥)与JWT(Token)。
JWT Secret (密钥):

  • 这是一个私有的字符串,仅在服务器端使用。
  • 它用来对Token进行签名,以确保Token的完整性和真实性。
  • 服务器在创建Token时使用这个密钥来签名,在接收到Token时也用它来验证Token的有效性。
  • 密钥应该保密,并且不应该暴露给客户端或第三方。

JWT (Token):

  • 每个用户登录成功后都会收到一个唯一的JWT。
  • JWT包含了一些声明(claims),比如用户的ID、过期时间等。
  • 客户端(例如浏览器或移动应用)会在后续请求中携带这个Token,通常是通过HTTP头部的Authorization字段。
  • 服务器会验证Token的签名,检查它的有效性和完整性,然后根据Token中的信息来识别用户身份。

接下来,需要考虑如何安全的生成一个密钥且放置在一个安全的地方~
将JWT密钥配置在环境变量中(如process.env.JWT_SECRET)是目前推荐的做法之一,但安全性取决于你如何管理和保护这些环境变量。以下是几个关键点来确保这种方式的安全性:

  1. 不要硬编码密钥
    避免将密钥直接写入代码库或配置文件中,因为这可能会导致它们被意外泄露。使用环境变量可以有效防止这种情况发生。

  2. 使用.env文件时要小心
    如果你选择使用.env文件来管理环境变量,请确保这个文件不会被提交到版本控制系统中。你应该在项目的根目录下创建一个.gitignore文件,并添加如下内容:

.env

这样可以确保.env文件不会被Git追踪和上传。

  1. 限制对环境变量的访问
    确保只有必要的进程和服务能够读取环境变量。在生产环境中,操作系统级别的权限设置和容器化技术(如Docker)可以帮助你更好地控制谁可以访问这些变量。

  2. 在生产环境中安全地传递环境变量
    对于生产部署,考虑使用云服务提供商的安全特性来管理秘密信息。例如:

  • AWS Secrets Manager 或 AWS Systems Manager Parameter Store
  • Azure Key Vault
  • Google Cloud Secret Manager
  • HashiCorp Vault
    这些服务允许你在不暴露敏感信息的情况下,安全地存储和检索密钥等重要数据。
  1. 定期更新密钥
    即使采取了所有预防措施,也应该定期更新你的JWT密钥。这有助于减少长期使用的同一密钥可能带来的风险。当更换密钥时,确保有适当的策略处理现有Token的有效性问题,比如实现刷新Token机制。

  2. 使用强随机密钥
    确保生成的JWT密钥足够强大且随机。你可以使用密码学安全的伪随机数生成器(CSPRNG)来创建密钥,而不是简单的字符串。

  3. 加密环境变量(可选)
    在某些情况下,你还可以选择加密环境变量本身,然后在应用程序启动时解密。不过这种方法增加了复杂性,通常只在高度敏感的应用场景中使用。

  4. 审计和监控
    定期审查和监控对环境变量的访问日志,以检测任何异常行为。这可以帮助你及时发现潜在的安全漏洞。

这里咱们的条件有限,就只模拟一下在环境变量中存储密钥吧。
先安装需要的插件

npm install jsonwebtoken bcryptjs

根目录下创建init-env.js,.gitignore(这个看项目是否提交到github咯,不提交的话可以不搞),init-env.js的作用就是没有.env文件的时候在根目录下自动生成一个,然后赋值

const fs = require('fs');
const crypto = require('crypto');if (!fs.existsSync('.env')) {const jwtSecret = crypto.randomBytes(32).toString('base64');fs.writeFileSync('.env', `JWT_SECRET=${jwtSecret}`);console.log('.env file created with JWT_SECRET.');
} else {console.log('.env file already exists.');
}

这里还需要在package.json中加入一段代码,项目运行之后就可以正常执行是否生成.env

 "scripts": {"test": "echo \"Error: no test specified\" && exit 1","predev": "node init-env.js", "dev": "nodemon app.js"},

.env 文件生成后就有一个变量咯

JWT_SECRET=xdHKwg41O6SjdRD61e/iCi9PYIVCtaIy430xo3wcXyk=

在app.js入口文件中也加上

const crypto = require('crypto');// 如果 .env 文件中没有 JWT_SECRET,则生成一个新的
if (!process.env.JWT_SECRET) {const newSecret = crypto.randomBytes(32).toString('base64');// 这里可以根据实际情况选择是否更新 .env 文件// fs.writeFileSync('.env', `JWT_SECRET=${newSecret}`, { flag: 'a' });process.env.JWT_SECRET = newSecret;
}

最后回到我们的路由中去进行处理,在登录的api方法中,添加登录成功后token的生成,其实token的值就是根据上面的JWT_SECRET 与用户信息结合生成

const jwt = require("jsonwebtoken");
// 用户登录
router.post("/users/login",[body("user.username").notEmpty().withMessage("用户名不能为空"),body("user.password").notEmpty().withMessage("密码不能为空").bail().custom(async (password, { req }) => {const user = await User.findOne({ username: req.body.user.username });if (!user) {return Promise.reject("账户或密码错误,请检查!");}// 比较提供的密码与数据库中存储的哈希密码const isMatch = await bcrypt.compare(password, user.password);if (!isMatch) {return Promise.reject("账户或密码错误,请检查!");}return true;}),],async (req, res, next) => {const errors = validationResult(req);if (!errors.isEmpty()) {return res.status(400).json({ errors: errors.array()[0] });};try {// 获取用户信息const user = await User.findOne({ username: req.body.user.username });// 创建tokenconst token = jwt.sign({ userId: user._id }, process.env.JWT_SECRET, {expiresIn: "1h",});// 返回tokenres.json({message: "登录成功",token,user: {id: user._id,username: user.username,},});} catch (error) {console.error(error);res.status(500).json({ message: "服务器错误" });}},userCtrl.login
);

然后,来到postman,我们尝试调用这个api,看看返回值

接下来,继续看看token的校验咯,按照原计划,我们将进一步完善其它的接口,将其它的接口的头部加上token进行传递,然后我们添加token校验。以获取当前登录用户信息为例。
1.创建middleware\authMiddleware.js,封装中间件

const jwt = require("jsonwebtoken");function authenticateToken(req, res, next) {const authHeader = req.headers["authorization"];const token = authHeader && authHeader.split(" ")[1]; // Bearer <token>if (!token) return res.sendStatus(401); // 如果没有token,返回401 Unauthorizedjwt.verify(token, process.env.JWT_SECRET, (err, user) => {if (err) return res.sendStatus(403); // 如果验证失败,返回403 Forbidden// 将用户信息附加到req对象上,以便后续路由使用req.user = user;next(); // 继续执行下一个中间件或路由处理函数});
}
module.exports = authenticateToken;

2.在controller/user.js文件中完善根据用户id获取用户信息的get API

// 获取当前登录用户的信息
exports.getCurUser = async (req, res) => {try {const user = await User.findById(req.body.user.id).select('-password');//获取除密码以外的信息if (!user) {return res.status(404).json({ message: "用户未找到" });}res.json({message: "获取用户信息成功",data: {id: user._id,username: user.username,email: user.email,},});} catch (error) {console.error(error);res.status(500).json({ message: "服务器错误" });}
};

3.在router\user.js文件中引入token的校验中间件方法

const authenticateToken  = require("../middleware/authMiddleware");
// 获取当前登录用户信息
router.get("/user",[body("user.id").notEmpty().withMessage("用户id不能为空")],authenticateToken,(req, res,next) => {const errors = validationResult(req);if (!errors.isEmpty()) {return res.status(400).json({ errors: errors.array()[0] });}next()},userCtrl.getCurUser
);
  1. 进行测试


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/854323.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ingsollrang英格索兰IC直流电动拧紧控制器维修

随着智能装配的概念逐渐在行业内推广,质量管理已成为实现智能装配过程中的一个重要环节,许多客户都有着数据记录、扭矩检测的需求,英格索兰的多种拧紧工具配合控制器,可以满足从基本拧紧到质量管理的一系列需求,真正实现高级装配。 一、ingsollrang英格索兰IC直流电动拧紧…

【笔记】一些简单、基础的东西

一些简单的东西存储大小bit:位,简写为 bbyte:字节,简写为 B;一个字节占 8 位。k:千(1000)K/Ki:千(1024)ASCII10:enter、32:space48:065:A97:a

进阶篇:3.2.3 DFM钣金-弯管件设计

管材最小弯曲半径: 自己生活会变的更开心

X.3 一维梁

X.3 一维梁 一维连续系统 ​​ 本图中,w表示梁在z方向的挠度(deflection,或位移),f表示每单元长度受到的横向力(transverse force),T表示弦(string)受到的张力。 对于一维张紧弦,其控制方程为: \[\begin{equation}T\frac{d^2w}{dx^2}+f\begin{pmatrix}x\end{pmatri…

11.21 每日总结(设计模式)

今天继续设计模式 简单单工厂模式定义:定义了一个创建对象的类,由这个类来封装实例化对象的行为。

kali启动报“piix4_smbus 0000:00:07.3: SMBus Host controller not enabled”

问题:piix4_smbus 0000:00:07.3: SMBus Host controller not enabled 解决办法:增大内存

游戏场景

需要用的场景需要拖进去 场景跳转 最简单 场景类 场景管理类 两个场景叠加 场景加载卡顿 换如下方法 异步加载多线程 场景管理类先引用 场景非自动跳转

【实用指南】Zabbix服务器性能警告分析与解决方案:Zabbix server: Utilization of icmp pinger processes over

前言:在监控系统的日常运维中,Zabbix作为一个强大的开源监控工具,帮助我们实时监控网络和应用状态。然而,当Zabbix服务器性能出现警告时,如icmp pinger进程利用率过高,这可能会影响监控数据的准确性和及时性。本文将为您提供一个详细的分析和解决方案,帮助您快速定位问题…

车企软件研发流程及质量把控解决方案

在“软件定义汽车”时代,车载软件的比重逐步提高,车载软件的研发流程决定着车载软件质量的稳定性和可控性。经纬恒润可面向OEM/TIER1结合多标准要求,如:ASPICE/CMMI/ISO26262/IATF16949质量体系,搭建、定义车载软件开发流程以及供方管控标准和流程。概述在“软件定义汽车”…

Springboot+Nacos项目

微服务 微服务(Microservices)是一种软件架构风格,他区别与单体架构,将拆分为多个小型的、独立的服务,每个服务都可以独立开发、部署和维护。这些服务通过轻量级的API进行通信。 Nacos简述 Nacos 用于发现、配置和管理微服务。nacos有2个核心功能,一个是注册中心,一个是…