当前位置:首页 > 文章列表 > 文章 > 前端 > TypeORM与NestJS密码哈希教程详解

TypeORM与NestJS密码哈希教程详解

2025-12-09 15:57:34 0浏览 收藏
推广推荐
免费电影APP ➜
支持 PC / 移动端,安全直达

文章小白一枚,正在不断学习积累知识,现将学习到的知识记录一下,也是将我的所得分享给大家!而今天这篇文章《TypeORM与NestJS密码自动哈希教程》带大家来了解一下##content_title##,希望对大家的知识积累有所帮助,从而弥补自己的不足,助力实战开发!


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

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

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

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

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

TypeORM实体生命周期钩子概览

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

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

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

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

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 < 60) 作为判断是否为明文密码的启发式方法,以避免对已经哈希过的密码再次哈希。更严谨的做法是在更新时只接收明文密码,或通过业务逻辑判断是否需要重新哈希。
  • 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 });
    // 当调用save()方法时,User实体中的@BeforeInsert()钩子将自动触发,对password进行哈希
    return this.usersRepository.save(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;
    // 当调用save()方法时,User实体中的@BeforeUpdate()钩子将自动触发,对newPasswordPlain进行哈希
    return this.usersRepository.save(user);
  }

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

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

注意事项与最佳实践

  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.save()或entityManager.save()方法时触发。如果您出于某种特殊原因需要使用repository.insert()或repository.update()方法,那么您将需要手动处理密码哈希。
    • save()方法会根据实体是否具有主键来判断是执行插入还是更新操作。如果实体没有主键,则执行插入;如果实体有主键,则执行更新。
  3. 密码更新处理:

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

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

总结

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

到这里,我们也就讲完了《TypeORM与NestJS密码哈希教程详解》的内容了。个人认为,基础知识的学习和巩固,是为了更好的将其运用到项目中,欢迎关注golang学习网公众号,带你了解更多关于的知识点!

支付宝密码找回教程详解支付宝密码找回教程详解
上一篇
支付宝密码找回教程详解
支付宝注销权限不足解决方法
下一篇
支付宝注销权限不足解决方法
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • ChatExcel酷表:告别Excel难题,北大团队AI助手助您轻松处理数据
    ChatExcel酷表
    ChatExcel酷表是由北京大学团队打造的Excel聊天机器人,用自然语言操控表格,简化数据处理,告别繁琐操作,提升工作效率!适用于学生、上班族及政府人员。
    3240次使用
  • Any绘本:开源免费AI绘本创作工具深度解析
    Any绘本
    探索Any绘本(anypicturebook.com/zh),一款开源免费的AI绘本创作工具,基于Google Gemini与Flux AI模型,让您轻松创作个性化绘本。适用于家庭、教育、创作等多种场景,零门槛,高自由度,技术透明,本地可控。
    3453次使用
  • 可赞AI:AI驱动办公可视化智能工具,一键高效生成文档图表脑图
    可赞AI
    可赞AI,AI驱动的办公可视化智能工具,助您轻松实现文本与可视化元素高效转化。无论是智能文档生成、多格式文本解析,还是一键生成专业图表、脑图、知识卡片,可赞AI都能让信息处理更清晰高效。覆盖数据汇报、会议纪要、内容营销等全场景,大幅提升办公效率,降低专业门槛,是您提升工作效率的得力助手。
    3484次使用
  • 星月写作:AI网文创作神器,助力爆款小说速成
    星月写作
    星月写作是国内首款聚焦中文网络小说创作的AI辅助工具,解决网文作者从构思到变现的全流程痛点。AI扫榜、专属模板、全链路适配,助力新人快速上手,资深作者效率倍增。
    4594次使用
  • MagicLight.ai:叙事驱动AI动画视频创作平台 | 高效生成专业级故事动画
    MagicLight
    MagicLight.ai是全球首款叙事驱动型AI动画视频创作平台,专注于解决从故事想法到完整动画的全流程痛点。它通过自研AI模型,保障角色、风格、场景高度一致性,让零动画经验者也能高效产出专业级叙事内容。广泛适用于独立创作者、动画工作室、教育机构及企业营销,助您轻松实现创意落地与商业化。
    3859次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议隐私政策
返回登录
  • 重置密码