Deprecated: imwpcache\f884414bce24ee67f\f73723ec7b1919fa5::__construct(): Implicitly marking parameter $YECBGYFECGEAFWHA as nullable is deprecated, the explicit nullable type must be used instead in /www/wwwroot/www.chuangxiangniao.com/wp-content/plugins/imwpcache-dist/build/f884414bce24ee67ff73723ec7b1919fa5.php on line 2

Deprecated: imwpcache\f884414bce24ee67f\f73723ec7b1919fa5::__construct(): Implicitly marking parameter $BBWFDDBHHYHDXXAB as nullable is deprecated, the explicit nullable type must be used instead in /www/wwwroot/www.chuangxiangniao.com/wp-content/plugins/imwpcache-dist/build/f884414bce24ee67ff73723ec7b1919fa5.php on line 2
深入理解Node.js中bcryptjs进行密码哈希与验证_创想鸟

深入理解Node.js中bcryptjs进行密码哈希与验证

深入理解node.js中bcryptjs进行密码哈希与验证

本文旨在解决Node.js应用中,使用bcrypt库进行密码哈希存储与用户输入密码验证时可能遇到的兼容性问题,并推荐使用纯JavaScript实现的bcryptjs库作为替代方案。通过详细的教程和代码示例,文章将指导开发者如何在注册和登录流程中安全、高效地实现密码的哈希与比对,确保用户认证的稳定性和安全性。

1. 密码安全的重要性与哈希原理

在现代Web应用中,用户密码的安全性至关重要。直接存储明文密码是极其危险的做法,一旦数据库泄露,所有用户账户都将面临风险。为了解决这一问题,我们通常采用密码哈希(Hashing)技术。密码哈希是将原始密码通过单向加密算法转换为一串固定长度的、不可逆的字符串。每次用户注册时,我们存储的是密码的哈希值而非明文;用户登录时,则将用户输入的密码进行哈希,然后与数据库中存储的哈希值进行比对。

bcrypt是一种流行的密码哈希算法,以其计算成本高(抗暴力破解)和内置盐值(Salt)而闻名。盐值是一个随机字符串,与密码一起进行哈希,确保即使两个用户设置了相同的密码,其哈希值也不同,从而有效抵御彩虹表攻击。

2. bcrypt与bcryptjs的选择:兼容性考量

在Node.js生态中,有两个主要的库用于实现bcrypt算法:

bcrypt: 这是一个Node.js原生插件,通常性能更优,因为它使用C++实现。然而,原生模块在不同操作系统、Node.js版本或编译环境下可能遇到兼容性问题,例如常见的“Cannot find module napi-v3/bcrypt_lib.node”错误,这会导致模块加载失败,进而影响密码的哈希和比对功能。bcryptjs: 这是一个纯JavaScript实现的bcrypt库,与bcrypt API兼容。由于它是纯JavaScript,因此避免了原生模块可能带来的兼容性问题,在大多数环境中都能稳定运行,是跨平台部署的更稳健选择。

考虑到兼容性和部署的便捷性,我们强烈推荐在Node.js应用中使用bcryptjs进行密码处理。

3. 安装bcryptjs

首先,需要在你的项目中安装bcryptjs库:

npm install bcryptjs

4. 实现用户注册(密码哈希)

在用户注册时,我们需要获取用户提供的明文密码,对其进行加盐哈希处理,然后将哈希后的密码存储到数据库中。

const express = require('express');const bcrypt = require('bcryptjs'); // 引入 bcryptjsconst jwt = require('jsonwebtoken');const mongoose = require('mongoose');const cors = require('cors');const secretKey = 'your-secret-key'; // 替换为你的JWT密钥const app = express();app.use(cors());app.use(express.json());// MongoDB connection URIconst uri = 'mongodb://localhost:27017/final-year-project';// Connect to the MongoDB databasemongoose  .connect(uri, { useNewUrlParser: true, useUnifiedTopology: true })  .then(() => {    console.log('Connected to the database');    app.listen(3000);    console.log('app connected on port 3000');  })  .catch((error) => {    console.error('Failed to connect to the database:', error);  });// Define the user schemaconst userSchema = new mongoose.Schema({  firstName: { type: String, required: true },  lastName: { type: String, required: true },  email: { type: String, required: true, unique: true },  role: { type: String, required: true },  password: { type: String, required: true },}, { collection: 'users' });// Define the user modelconst User = mongoose.model('User', userSchema);class AuthResponseData {  constructor(user) {    this.user = user;  }}// Signup endpointapp.post('/signup', async (req, res) => {  try {    const { firstName, lastName, email, role, password } = req.body;    const existingUser = await User.findOne({ email });    if (existingUser) {      return res.status(400).json({ message: 'Email already exists' });    }    let plainTextPassword = password;    if (!plainTextPassword) {      plainTextPassword = 'defaultPassword123'; // 建议在实际应用中避免设置默认密码    }    // 使用 bcryptjs 进行密码哈希    // genSaltSync(10) 生成盐值,10是盐值轮数,影响哈希强度和计算时间    const hashedPassword = await bcrypt.hash(plainTextPassword, 10);     const newUser = new User({      firstName,      lastName,      email,      role,      password: hashedPassword, // 存储哈希后的密码    });    await newUser.save();    const token = jwt.sign({ email: newUser.email }, secretKey, { expiresIn: '1h' }); // 设置JWT过期时间    const expirationDate = new Date().getTime() + 3600000;    const user = {      firstName: newUser.firstName,      lastName: newUser.lastName,      email: newUser.email,      role: newUser.role,      id: newUser._id,      _token: token,      _tokenExpirationDate: expirationDate,    };    const authResponse = new AuthResponseData(user);    res.status(201).json(authResponse);  } catch (error) {    console.error('Signup error:', error);    res.status(500).json({ message: 'Internal server error' });  }});

代码解析:

bcrypt.hash(plainTextPassword, 10):这是bcryptjs的核心方法。它接收两个参数:明文密码和盐值轮数(或一个已生成的盐值)。轮数越高,哈希过程越慢,安全性越高,但也会增加服务器负载。通常建议使用10到12。这个方法是异步的,返回一个Promise,所以我们使用await来等待哈希完成。我们将生成的hashedPassword存储到数据库的password字段中。

5. 实现用户登录(密码比对)

在用户登录时,我们需要从数据库中获取存储的哈希密码,然后将用户输入的明文密码与这个哈希密码进行比对。

// Login endpointapp.post('/login', async (req, res) => {  try {    const { email, password } = req.body;    // 1. 查找用户    const user = await User.findOne({ email });    if (!user) {      return res.status(401).json({ message: 'Invalid email or password' });    }    // 2. 比对密码    // bcrypt.compare(plainTextPassword, hashedPasswordFromDb)    const passwordMatch = await bcrypt.compare(password, user.password);    if (!passwordMatch) {      return res.status(401).json({ message: 'Invalid email or password' });    }    // 3. 生成JWT并返回认证信息    const token = jwt.sign({ email: user.email }, secretKey, { expiresIn: '1h' });    const expirationDate = new Date().getTime() + 3600000;    const loggedInUser = {      firstName: user.firstName,      lastName: user.lastName,      email: user.email,      role: user.role,      id: user._id,      _token: token,      _tokenExpirationDate: expirationDate,    };    const authResponse = new AuthResponseData(loggedInUser);    res.status(200).json(authResponse);  } catch (error) {    console.error('Login error:', error);    res.status(500).json({ message: 'Internal server error' });  }});

代码解析:

bcrypt.compare(password, user.password):这是bcryptjs用于比对密码的核心方法。它接收两个参数:用户输入的明文密码和从数据库中取出的哈希密码。它会自动提取哈希密码中的盐值,并使用该盐值对明文密码进行哈希,然后比对两个哈希值是否一致。这个方法也是异步的,返回一个Promise,所以我们使用await来等待比对结果。如果passwordMatch为true,则表示密码正确,用户可以登录;否则,密码不匹配。

6. 注意事项与最佳实践

错误处理: 在实际应用中,务必对bcrypt.hash和bcrypt.compare可能抛出的错误进行捕获和处理,以防止应用崩溃并向用户返回友好的错误信息。盐值轮数: 选择合适的盐值轮数(cost factor)很重要。轮数越高,安全性越强,但哈希计算所需的时间也越长。通常,10到12是一个很好的平衡点。随着硬件性能的提升,可能需要逐渐增加轮数。异步操作: bcryptjs的hash和compare方法都是异步的。在Node.js环境中,应始终使用它们的异步版本(通过回调函数或Promise/async/await),以避免阻塞事件循环,影响服务器性能。JWT密钥安全: secretKey用于签署和验证JWT,必须妥善保管,不能硬编码在代码中。建议从环境变量中读取,并确保其足够复杂和随机。默认密码: 在注册流程中,避免为用户设置默认密码。如果用户未提供密码,应提示其输入或拒绝注册请求。

总结

通过使用bcryptjs库,我们能够安全、可靠地在Node.js应用中实现密码的哈希存储与验证。bcryptjs作为纯JavaScript实现,有效规避了原生bcrypt库可能带来的兼容性问题,为开发者提供了一个稳定且易于集成的解决方案。遵循上述教程和最佳实践,可以显著提升Web应用的用户认证安全性。

以上就是深入理解Node.js中bcryptjs进行密码哈希与验证的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/1522874.html

赞 (0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
Node.js 中使用 bcryptjs 安全地存储与验证用户密码
上一篇 2025年12月20日 15:25:40
安全地比较存储的哈希密码与用户输入的密码
下一篇 2025年12月20日 15:25:50

相关推荐

  • VSCode 怎样设置编辑器的字体连写效果 VSCode 字体连写效果的创意设置教程​

    要让vscode支持字体连写,需先安装支持连写的字体如fira code,再在settings.json中配置”editor.fontfamily”并将”editor.fontligatures”设为true,最后重启vscode验证效果;若不生效,检…

    2026年9月25日
    1200
  • 使用 Jackson 进行复杂类的自定义反序列化

    使用 Jackson 进行复杂类的自定义反序列化使用 Jackson 进行复杂类的自定义反序列化使用 Jackson 进行复杂类的自定义反序列化使用 Jackson 进行复杂类的自定义反序列化

    本文介绍了如何使用 Jackson 库对包含复杂嵌套类的 JSON 字符串进行自定义反序列化。通过 ObjectMapper 的 readValue 方法可以实现简单场景下的自动反序列化。针对需要定制化处理的场景,可以结合 ObjectMapper 和自定义反序列化器来实现更灵活的反序列化逻辑,并提…

    2026年9月25日 • 用户投稿
    900
  • sublime怎么使用命令面板(command palette)_sublime命令面板使用与快捷命令说明

    sublime怎么使用命令面板(command palette)_sublime命令面板使用与快捷命令说明sublime怎么使用命令面板(command palette)_sublime命令面板使用与快捷命令说明sublime怎么使用命令面板(command palette)_sublime命令面板使用与快捷命令说明sublime怎么使用命令面板(command palette)_sublime命令面板使用与快捷命令说明

    命令面板是Sublime Text高效操作核心,通过Ctrl+Shift+P(Win/Linux)或Cmd+Shift+P(macOS)打开,输入关键词如theme、syntax、package可快速执行更换主题、设置语法、安装插件等命令,支持动态搜索与回车执行,结合常用命令如设置修改、快捷键调整、…

    2026年9月25日 • 用户投稿
    400
  • DeepSeek部署在云服务器时要注意什么 部署环境配置及安全策略建议

    DeepSeek部署在云服务器时要注意什么 部署环境配置及安全策略建议DeepSeek部署在云服务器时要注意什么 部署环境配置及安全策略建议DeepSeek部署在云服务器时要注意什么 部署环境配置及安全策略建议DeepSeek部署在云服务器时要注意什么 部署环境配置及安全策略建议

    当计划将DeepSeek部署到云服务器上时,细致周到的准备工作对于确保系统的稳定运行和数据安全至关重要。本文将详细介绍在云端部署DeepSeek时需要特别注意的环境配置和安全策略。我们将探讨如何选择合适的服务器资源、安装必要的软件依赖,并实施一系列关键的安全措施,旨在为您提供清晰的操作指导,帮助您顺…

    2026年9月25日 • 用户投稿
    900
  • 使用 JSON Schema 中的 oneOf 避免无效枚举值的错误提示

    使用 JSON Schema 中的 oneOf 避免无效枚举值的错误提示使用 JSON Schema 中的 oneOf 避免无效枚举值的错误提示使用 JSON Schema 中的 oneOf 避免无效枚举值的错误提示使用 JSON Schema 中的 oneOf 避免无效枚举值的错误提示

    本文旨在帮助开发者理解并解决在使用 JSON Schema 验证 JSON 数据时,由于 oneOf 约束可能导致的“无效枚举值”的错误提示问题。我们将通过示例代码,详细解释错误产生的原因,并提供相应的解决方案,以确保 JSON Schema 验证的准确性和可读性。 在使用 JSON Schema …

    2026年9月25日 • 用户投稿
    500
  • Debian Hadoop数据传输优化方法

    Debian Hadoop数据传输优化方法Debian Hadoop数据传输优化方法Debian Hadoop数据传输优化方法Debian Hadoop数据传输优化方法

    提升Debian Hadoop集群数据传输效率的关键在于多方面策略的综合运用。本文将详细阐述优化方法,助您显著提升集群性能。 一、数据本地化策略 最大限度地将计算任务分配至数据存储节点,有效减少节点间数据传输。Hadoop的数据本地化机制会自动将数据块移动到计算任务所在的节点,从而避免网络传输带来的…

    2026年9月25日 • 用户投稿
    200
  • p5.js WebGL性能优化:首帧渲染耗时长的原因与对策

    p5.js WebGL性能优化:首帧渲染耗时长的原因与对策p5.js WebGL性能优化:首帧渲染耗时长的原因与对策p5.js WebGL性能优化:首帧渲染耗时长的原因与对策p5.js WebGL性能优化:首帧渲染耗时长的原因与对策

    在使用p5.js的WEBGL渲染模式时,首次调用image()函数渲染图片或p5.Graphics对象通常会比后续调用耗时显著增加。这主要是因为第一次渲染时,p5.js需要将图像数据从CPU内存上传到GPU的纹理内存中,涉及内存分配和数据复制,这是一个相对耗时的过程。后续调用由于纹理已被缓存,可以直…

    2026年9月25日 • 用户投稿
    700
  • 为什么不同浏览器对硬件加速的实现存在差异?

    不同浏览器因渲染引擎、图形API及权衡策略差异导致硬件加速表现不同。1. Blink、Gecko、WebKit引擎在图层管理与GPU任务分配上设计不同;2. 各浏览器通过ANGLE等抽象层适配DirectX、Vulkan、Metal,转换开销与支持程度影响性能;3. 厂商在性能、兼容性、稳定性间取舍…

    2026年9月25日
    100
  • 快手直播间怎么装修_快手直播间装修的实用方法与建议

    快手直播间怎么装修_快手直播间装修的实用方法与建议快手直播间怎么装修_快手直播间装修的实用方法与建议快手直播间怎么装修_快手直播间装修的实用方法与建议快手直播间怎么装修_快手直播间装修的实用方法与建议

    明确直播主题、优化灯光布局、设计简洁背景墙、合理规划功能区及改善声网环境是提升快手直播间专业度的关键。首先根据内容类型确定风格,如美妆选柔和色调,游戏用科技感灯光;参考热门主播布置并保持视觉统一。主光源采用4500K环形灯,辅以侧补光和背景灯带增强层次。背景选用低饱和纯色墙,搭配品牌LOGO或绿植,…

    2026年9月25日 • 用户投稿
    100
  • 这台五万元的相机,哈苏想卖给「普通人」

    这台五万元的相机,哈苏想卖给「普通人」这台五万元的相机,哈苏想卖给「普通人」这台五万元的相机,哈苏想卖给「普通人」这台五万元的相机,哈苏想卖给「普通人」

    拍照,可能是这个时代门槛最低的创作行为了。 我们每天都在生产和消费着海量的图片,记录变得前所未有地容易,但容易,就等于好吗? 过去,哈苏的答案是倾向于「好」,但代价是「难」——你需要理解光圈、快门,要背着沉重的三脚架,甚至要在特定的拍摄环境中,才能驾驭这份极致的画质。 在推出了备受瞩目的 X2D 1…

    2026年9月25日 • 用户投稿
    200
  • C语言判断素数方法

    C语言判断素数方法C语言判断素数方法C语言判断素数方法C语言判断素数方法

    求素数的问题通常可以划分为两大类。 1、解决素数相关问题常用的方法主要有两种。 2、判断某个给定的数是否为质数。 3、找出所有小于指定数值的质数。 4、核心概念包括:素数是大于1且只能被1和其本身整除的自然数。要判断一个数是否为素数,可以通过尝试用从2到该数减1的所有整数去除它,若发现有能整除的因子…

    2026年9月25日 • 用户投稿
    700
  • 豆包是否可以本地部署 自主可控环境下运行豆包的技术路径说明

    本文旨在解答关于豆包是否可以在本地环境下进行部署并实现自主可控运行的问题。目前,豆包主要以云服务形式提供,用户通过网络访问其功能。要在自主可控的环境下运行类似的大型语言模型能力,通常需要采用不同的技术路径,即在本地计算资源上部署可用的AI模型。本文将概述实现本地自主可控AI运行的通用技术路线和关键步…

    2026年9月25日
    300
  • sublime怎么配置eslint进行js校验_sublime集成ESLint代码检查配置

    sublime怎么配置eslint进行js校验_sublime集成ESLint代码检查配置sublime怎么配置eslint进行js校验_sublime集成ESLint代码检查配置sublime怎么配置eslint进行js校验_sublime集成ESLint代码检查配置sublime怎么配置eslint进行js校验_sublime集成ESLint代码检查配置

    首先安装SublimeLinter和SublimeLinter-eslint插件,确保系统或项目中已安装ESLint;通过npx eslint –init生成配置文件;插件会自动调用项目内的eslint,若未识别可手动设置executable路径;保存JavaScript文件时即可实时显…

    2026年9月25日 • 用户投稿
    000
  • PHP加密算法有哪些_PHP数据加密解密常用函数

    推荐使用password_hash()存储密码,openssl_encrypt()加密数据,RSA实现安全通信,根据场景选择合适加密方式保障信息安全。 在PHP开发中,数据加密和解密是保障信息安全的重要手段。根据使用场景的不同,可以选择不同的加密方式。常见的需求包括密码存储、敏感数据传输、配置文件加…

    2026年9月25日
    000
  • Java 8 使用 Stream API 扁平化嵌套 Map 并提取首个元素

    Java 8 使用 Stream API 扁平化嵌套 Map 并提取首个元素Java 8 使用 Stream API 扁平化嵌套 Map 并提取首个元素Java 8 使用 Stream API 扁平化嵌套 Map 并提取首个元素Java 8 使用 Stream API 扁平化嵌套 Map 并提取首个元素

    本文将详细介绍如何使用 Java 8 的 Stream API 将一个嵌套的 Map 结构进行扁平化处理,并从中提取所需的数据。 具体来说,我们将把 Map<Integer, Map<String, List>> 转换为 Map,其中新 Map 的键是原内部 Map 的键,值…

    2026年9月25日 • 用户投稿
    1200
  • 对话逐际动力张巍:造机器人很容易,关键是用起来

    对话逐际动力张巍:造机器人很容易,关键是用起来对话逐际动力张巍:造机器人很容易,关键是用起来对话逐际动力张巍:造机器人很容易,关键是用起来对话逐际动力张巍:造机器人很容易,关键是用起来

    “让天下没有难落地的机器人。” 在这样向量子位表达定位和使命后,逐际动力”解释了”为何会成为阿里投资的第一家具身智能机器人公司。 在这样解释定位和使命后,量子位大概感受到了逐际动力被投资的原因—— 至少是成为阿里第一个具身智能投资项目的原因。 实际上,…

    2026年9月25日 • 用户投稿
    500
  • Word文档全选文本怎么做_Word文档全选文本如何做详细方法

    Word文档全选文本怎么做_Word文档全选文本如何做详细方法Word文档全选文本怎么做_Word文档全选文本如何做详细方法Word文档全选文本怎么做_Word文档全选文本如何做详细方法Word文档全选文本怎么做_Word文档全选文本如何做详细方法

    全选Word文档最常用方法是使用快捷键Ctrl+A(Windows)或Command+A(Mac),可快速选中全部内容;也可通过“开始”选项卡中“编辑”组的“选择”命令进行全选;小文档可用鼠标拖动选中;在分节或多栏文档中需确保页面视图并尝试双击“全选”以避免遗漏,推荐优先使用快捷键操作。 在Word…

    2026年9月25日 • 用户投稿
    600
  • 修改 Android KeyStore 中 KeyPair 的用途

    修改 Android KeyStore 中 KeyPair 的用途修改 Android KeyStore 中 KeyPair 的用途修改 Android KeyStore 中 KeyPair 的用途修改 Android KeyStore 中 KeyPair 的用途

    本文档介绍了如何在 Android KeyStore 中修改现有 KeyPair 的用途,使其支持密钥协商 (Key Agreement) 操作。通过示例代码展示了如何利用 KeyStore.setEntry 方法在 Android 13 (API 33) 及以上版本中导入 KeyPair 并设置所…

    2026年9月25日 • 用户投稿
    600
  • sublime怎么跳转到指定行_sublime快速定位行号的方法

    sublime怎么跳转到指定行_sublime快速定位行号的方法sublime怎么跳转到指定行_sublime快速定位行号的方法sublime怎么跳转到指定行_sublime快速定位行号的方法sublime怎么跳转到指定行_sublime快速定位行号的方法

    跳转到指定行可通过快捷键实现:Windows/Linux按Ctrl + G,Mac按Cmd + G,弹出对话框后输入行号(如50)或行:列(如50:10)即可定位,状态栏双击亦可打开该对话框,还可通过命令面板输入“goto line”进行跳转,其中快捷键方式最快捷。 在 Sublime Text 中…

    2026年9月25日 • 用户投稿
    700
  • sublime怎么查看和安装主题_Sublime主题安装与切换美化教程

    sublime怎么查看和安装主题_Sublime主题安装与切换美化教程sublime怎么查看和安装主题_Sublime主题安装与切换美化教程sublime怎么查看和安装主题_Sublime主题安装与切换美化教程sublime怎么查看和安装主题_Sublime主题安装与切换美化教程

    在Sublime Text中更换和安装主题能显著提升编码体验,让界面更美观、更符合个人习惯。下面详细介绍如何查看、安装以及切换主题。 查看当前使用的主题 要确认当前Sublime Text正在使用哪个主题,可以打开命令面板: 按下 Ctrl+Shift+P(Windows/Linux)或 Cmd+S…

    2026年9月25日 • 用户投稿
    600

发表回复

登录后才能评论
关注微信