TypeORM与NestJS应用中实现用户密码自动哈希的教程


TypeORM与NestJS应用中实现用户密码自动哈希的教程

本教程详细介绍了如何在typeorm与nestjs应用中,利用实体生命周期钩子(如`@beforeinsert()`和`@beforeupdate()`)实现用户密码的自动哈希。通过在用户实体中集成`bcrypt`库,我们可以在保存用户模型时,无需手动干预,自动将明文密码转换为安全的哈希值,确保数据存储的安全性与便捷性。

引言:保障用户密码安全与自动化处理

在任何用户认证系统中,妥善存储用户密码是至关重要的安全实践。直接存储明文密码是极其危险的,一旦数据库泄露,所有用户账户都将面临风险。因此,对密码进行哈希处理是行业标准做法。为了提升开发效率并确保一致性,我们通常希望在用户模型持久化到数据库时,能够自动完成密码的哈希过程,而无需在每次保存操作时手动调用哈希函数。

TypeORM作为一款强大的ORM框架,提供了实体生命周期钩子(Entity Lifecycle Hooks),这些钩子允许我们在实体被插入、更新或删除等操作前后执行自定义逻辑。结合NestJS的模块化架构,我们可以优雅地实现密码的自动哈希。

TypeORM实体生命周期钩子概览

TypeORM提供了一系列装饰器,用于标记在特定数据库操作前后执行的方法。其中,对于密码哈希场景,@BeforeInsert()和@BeforeUpdate()是核心。

  • @BeforeInsert(): 在实体首次插入数据库之前执行。这非常适合在新用户注册时哈希其密码。
  • @BeforeUpdate(): 在实体更新到数据库之前执行。如果用户有修改密码的功能,此钩子可以确保新密码也被正确哈希。

这些钩子会在使用TypeORM的repository.s*e()或entityManager.s*e()方法持久化实体时自动触发。需要注意的是,如果直接使用repository.insert()方法,这些钩子将不会被触发,因为insert()方法旨在提供更底层的、性能导向的插入,它会绕过ORM的一些高级特性,包括生命周期事件。因此,在需要触发实体生命周期钩子的场景下,应优先使用s*e()方法。

实现步骤:在用户实体中集成自动密码哈希

1. 安装密码哈希库

我们将使用bcrypt库进行密码哈希。bcrypt是一种流行的、安全的密码哈希函数,它设计为计算密集型,以抵御彩虹表攻击和暴力破解。

首先,在您的NestJS项目中安装bcrypt:

npm install bcrypt
npm install -D @types/bcrypt

2. 配置用户实体

接下来,修改您的用户实体(例如User.entity.ts),在其中添加password属性,并实现一个带有@BeforeInsert()和@BeforeUpdate()装饰器的方法来处理密码哈希。

import { Entity, PrimaryGeneratedColumn, Column, BeforeInsert, BeforeUpdate } from 'typeorm';
import * as bcrypt from 'bcrypt';

@Entity()
export class User {
  @PrimaryGeneratedColumn()
  id: number;

  @Column({ unique: true })
  username: string;

  @Column()
  password: string;

  @Column({ default: true })
  isActive: boolean;

  /**
   * 在插入数据库之前哈希密码
   * 如果password属性存在且不为空,则对其进行哈希处理
   */
  @BeforeInsert()
  async hashPasswordOnInsert() {
    if (this.password) {
      this.password = await bcrypt.hash(this.password, 10); // 10是盐值轮次,建议在8-12之间
    }
  }

  /**
   * 在更新数据库之前哈希密码
   * 仅当password属性被修改时才进行哈希处理
   */
  @BeforeUpdate()
  async hashPasswordOnUpdate() {
    // 可以在此处添加逻辑,仅当密码实际发生变化时才进行哈希
    // 例如:通过比较当前密码哈希值与数据库中的旧值,但这需要额外查询
    // 更简单的做法是,如果前端发送了新的明文密码,就重新哈希
    if (this.password && this.password.length < 60) { // 假设哈希后的密码长度远大于60,用于区分明文密码
      this.password = await bcrypt.hash(this.password, 10);
    }
  }
}

在上述代码中:

  • @BeforeInsert()装饰的hashPasswordOnInsert方法会在每次创建新用户并保存时触发。
  • @BeforeUpdate()装饰的hashPasswordOnUpdate方法会在每次更新现有用户并保存时触发。我们添加了一个简单的长度检查 (this.password.length
  • bcrypt.hash(this.password, 10):10是盐值轮次(salt rounds),它决定了哈希计算的复杂程度。值越大,哈希越安全,但计算时间也越长。通常建议在8到12之间。

3. 在服务层使用

在您的用户服务(例如UserService)中,您可以像往常一样创建和保存用户,无需在服务层手动调用哈希函数。哈希过程将由TypeORM的实体钩子自动处理。

import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { User } from './user.entity';

@Injectable()
export class UserService {
  constructor(
    @InjectRepository(User)
    private usersRepository: Repository<User>,
  ) {}

  async createUser(username: string, passwordPlain: string): Promise<User> {
    const newUser = this.usersRepository.create({ username, password: passwordPlain });
    // 当调用s*e()方法时,User实体中的@BeforeInsert()钩子将自动触发,对password进行哈希
    return this.usersRepository.s*e(newUser);
  }

  async updateUserPassword(userId: number, newPasswordPlain: string): Promise<User | undefined> {
    const user = await this.usersRepository.findOne({ where: { id: userId } });
    if (!user) {
      return undefined;
    }
    user.password = newPasswordPlain;
    // 当调用s*e()方法时,User实体中的@BeforeUpdate()钩子将自动触发,对newPasswordPlain进行哈希
    return this.usersRepository.s*e(user);
  }

  // ... 其他用户相关方法
}

通过这种方式,UserService中的代码保持简洁,关注于业务逻辑,而密码哈希的底层实现则封装在实体内部。

芦笋演示 芦笋演示

一键出成片的录屏演示软件,专为制作产品演示、教学课程和使用教程而设计。

芦笋演示 227 查看详情 芦笋演示

注意事项与最佳实践

  1. 安全性考量:

    • 选择合适的盐值轮次: bcrypt的盐值轮次应该根据您的服务器性能和安全需求进行调整。更高的轮次意味着更强的安全性,但也需要更多的计算资源。

    • 永远不要存储盐值: bcrypt生成的哈希值本身就包含了盐值,因此您无需单独存储它。

    • 密码比对: 当用户尝试登录时,您需要将用户输入的明文密码与数据库中存储的哈希密码进行比对。bcrypt提供了compare方法来完成此操作:

      import * as bcrypt from 'bcrypt';
      
      async function validatePassword(plainPassword: string, hashedPasswordFromDb: string): Promise<boolean> {
        return bcrypt.compare(plainPassword, hashedPasswordFromDb);
      }
  2. 钩子的触发时机:

    • 再次强调,@BeforeInsert()和@BeforeUpdate()钩子仅在使用repository.s*e()或entityManager.s*e()方法时触发。如果您出于某种特殊原因需要使用repository.insert()或repository.update()方法,那么您将需要手动处理密码哈希。
    • s*e()方法会根据实体是否具有主键来判断是执行插入还是更新操作。如果实体没有主键,则执行插入;如果实体有主键,则执行更新。
  3. 密码更新处理:

    • 在@BeforeUpdate()中,判断是否需要重新哈希密码的逻辑需要谨慎。上述示例中的长度检查是一种简易方式,但更健壮的方法可能是在更新操作时,明确只在接收到明文密码时才进行哈希。例如,如果您的更新DTO只包含需要更新的字段,并且password字段仅在用户请求修改密码时才包含明文,那么直接哈希即可。
  4. 性能:

    • bcrypt是一个计算密集型操作,尤其是在高盐值轮次下。对于高并发的注册或密码更新场景,这可能会对服务器性能产生轻微影响。然而,对于大多数Web应用而言,这种开销是可接受的,并且是保障安全性的必要代价。bcrypt.hash是异步的,因此不会阻塞Node.js事件循环。

总结

通过在TypeORM实体中巧妙利用@BeforeInsert()和@BeforeUpdate()生命周期钩子,并结合bcrypt库,我们能够实现NestJS应用中用户密码的自动化哈希。这种方法不仅提高了代码的内聚性和可维护性,将密码安全逻辑封装在实体内部,还确保了每次密码持久化时都能自动进行安全的哈希处理,极大地简化了开发流程并增强了系统的安全性。遵循这些实践,您的NestJS应用将拥有一个健壮且安全的密码管理机制。

以上就是TypeORM与NestJS应用中实现用户密码自动哈希的教程的详细内容,更多请关注其它相关文章!


# js  # word  # 时才  # 您的  # 用户注册  # ai  # npm  # node  # node.js  # 前端  # 开福区营销推广怎么做  # 盘锦网站优化服务哪家好  # 湖南seo软件代理  # 是在  # 郑州网络营销推广工具  # 河源软文营销推广  # seo伪静态链接  # 我们可以  # 主键  # 是一种  # 文档  # 如何实现  # 会在  # 第三方推广网站怎么做  # 新乡营销推广合作  # seo咖啡肯德基  # 可乐营销推广方案 


相关栏目: 【 Google疑问12 】 【 Facebook疑问10 】 【 优化推广96088 】 【 技术知识133117 】 【 IDC资讯59369 】 【 网络运营7196 】 【 IT资讯61894 】


相关推荐: 韩剧圈正版官网入口_韩剧圈官方指定登录  铁拳8在线玩 铁拳8在线秒玩入口  微信注销后银行卡解绑了吗_微信注销后银行卡解绑状态  毒蘑菇VOLUMESHADER_BM官网首页登录入口 毒蘑菇VOLUMESHADER_BM官网首页登录入口说明  解决Pandas DataFrame高度碎片化警告:高效创建多列的策略  《原神》月之一版本新增书籍一览  纯CSS实现自适应宽度与响应式布局的水平按钮组  优化 WooCommerce 产品价格显示与自定义短代码集成  《崩坏:星穹铁道》3.6版本异相仲裁打法及配队推荐  优化 React onClick 事件处理:函数引用与箭头函数的对比  《植物大战僵尸3》火龙草作用介绍  猫眼电影app怎么查询电影院的营业时间_猫眼电影影院营业时间查询教程  小米手机屏幕失灵乱跳怎么办 屏幕触控问题自检与临时解决方法【应急】  Flexbox布局实践:实现底部页脚与顶部粘性导航条的完美结合  mysql数据库索引类型有哪些_mysql索引类型解析  Excel如何快速找到并断开外部数据源链接_Excel外部数据源断开方法  构建可配置的J*aScript加权点击计数器与共享总计功能  抖音评论无法发送如何修复 抖音评论功能操作指南  微博网页版入口链接 微博网页版在线互动平台  如何取消数字签名  qq邮箱格式填写示例 qq邮箱标准填写规范  泰拉瑞亚水晶无法放置问题  Win10通知横幅停留时间修改 Win10自定义通知显示时长【技巧】  b站怎么设置动态仅粉丝可见_b站动态粉丝可见设置方法  背部总是隐隐作痛怎么回事 背痛如何改善  邦丰播放器频道搜索设置  Python高效统计字典嵌套列表值在目标列表中的出现次数  如何自定义苹果手机铃声  HTML中多图片上传与预览:解决ID冲突的专业指南  追剧达人如何发弹幕  Final Cut Pro视频加EQ教程  C++中的explicit关键字有什么作用_C++类型转换控制与explicit使用  Golang如何测试结构体方法_Golang reflect方法测试与调用技巧  Safari浏览器自动填表功能失效怎么办 Safari表单管理修复  windows10怎么开启wsl_windows10安装linux子系统教程  汽车之家网页版免费登录_汽车之家官网首页直接进入  百度网盘网页入口链接分享 百度网盘官网入口网页登录  @Team是什么?揭秘团队含义  139邮箱登录入口官网 139邮箱登录入口官网网址  向日葵客户端怎么进行语音通话_向日葵客户端语音通话功能使用方法  在Peewee中处理PostgreSQL记录重复:一站式数据摄取教程  告别阻塞等待:如何使用GuzzlePromises优雅处理PHP异步操作,提升应用响应速度  如何解决Casbin日志与应用日志不统一的问题,使用casbin/psr3-bridge实现无缝集成  处理含命名空间的XML文件 Power Query中的高级技巧  优化Flask模板中SQLAlchemy查询迭代标签:处理字符串空格问题  一加 Ace 6V 快充无法启用_一加 Ace 6V 充电优化  微星主板BIOS怎么调整内存时序_内存参数手动优化BIOS设置教程  极兔快递官网查询入口手机版 手机极兔快递登录查询入口官方  鸣潮历史学家灯塔位置一览  飞飞漫画漫画阅读官网_飞飞漫画漫画阅读官网进入阅读 

 2025-12-05

了解您产品搜索量及市场趋势,制定营销计划

同行竞争及网站分析保障您的广告效果

点击免费数据支持

提交您的需求,1小时内享受我们的专业解答。

运城市盐湖区信雨科技有限公司


运城市盐湖区信雨科技有限公司

运城市盐湖区信雨科技有限公司是一家深耕海外推广领域十年的专业服务商,作为谷歌推广与Facebook广告全球合作伙伴,聚焦外贸企业出海痛点,以数字化营销为核心,提供一站式海外营销解决方案。公司凭借十年行业沉淀与平台官方资源加持,打破传统外贸获客壁垒,助力企业高效开拓全球市场,成为中小企业出海的可靠合作伙伴。

 8156699

 13765294890

 8156699@qq.com

Notice

We and selected third parties use cookies or similar technologies for technical purposes and, with your consent, for other purposes as specified in the cookie policy.
You can consent to the use of such technologies by closing this notice, by interacting with any link or button outside of this notice or by continuing to browse otherwise.