Nest.js权限管理系统开发(七)用户注册

创建user模块

先用nest的命令创建一个 user 模块,

nest g res user

实现user实体

然后就生成了 user 模块,在它的实体中创建一个用户表user.entity.ts,包含 id、用户名、密码,头像、邮箱等等一些字段:

@Entity('sys_user')
export class UserEntity {
  @Expose()
  @ApiProperty({ type: String, description: 'id' })
  @PrimaryGeneratedColumn({ type: 'bigint' })
  public id: string

  @Exclude({ toPlainOnly: true }) // 输出屏蔽密码
  @Column({ type: 'varchar', length: 200, nullable: false, comment: '用户登录密码' })
  public password: string

  @Exclude({ toPlainOnly: true }) // 输出屏蔽盐
  @Column({ type: 'varchar', length: 200, nullable: false, comment: '盐' })
  public salt: string

  @Expose()
  @ApiProperty({ type: String, description: '用户登录账号' })
  @Column({ type: 'varchar', length: 32, comment: '用户登录账号' })
  public account: string

  @Expose()
  @ApiProperty({ type: String, description: '手机号' })
  @Column({ type: 'varchar', name: 'phone_num', default: '', length: 20, comment: '用户手机号码' })
  public phoneNum: string

  @Expose()
  @ApiProperty({ type: String, description: '邮箱' })
  @Column({ type: 'varchar', comment: '邮箱地址', default: '' })
  public email: string

  ....
}

实现注册路由

接下来看一下注册基本逻辑的实现。注册无非就是新增一个用户,在user.controller.ts规定一个路由/user/register接收用户传来的参数

@Post('register')
  @ApiOperation({ summary: '用户注册' })
  @ApiResult(UserEntity)
  @AllowAnon()
  async create(@Body() user: CreateUserDto): Promise<UserEntity> {
    return await this.userService.create(user)
  }

并将CreateUserDto实现如下:

export class CreateUserDto {
  @ApiProperty({ description: '用户账号' })
  @IsString({ message: 'account 类型错误,正确类型 string' })
  @IsNotEmpty({ message: 'account 不能为空' })
  @MinLength(5, { message: '账号至少5个字符' })
  @MaxLength(20, { message: '账号最多20个字符' })
  readonly account: string

  @ApiProperty({ description: '密码' })
  @IsString({ message: 'password 类型错误,正确类型 string' })
  @IsNotEmpty({ message: 'password 不能为空' })
  password: string

  @ApiProperty({ description: '手机号', required: false })
  @IsString({ message: 'phoneNum 类型错误,正确类型 string' })
  @IsMobilePhone('zh-CN', { strictMode: false }, { message: '请输入正确的手机号' })
  @IsOptional()
  readonly phoneNum?: string

  @ApiProperty({ description: '邮箱', required: false })
  @IsString({ message: 'email 类型错误,正确类型 string' })
  @IsEmail()
  @IsOptional()
  readonly email?: string

  @ApiProperty({ description: '确认密码' })
  @IsString({ message: ' confirmPassword 类型错误,正确类型 string' })
  readonly confirmPassword: string

  @ApiProperty({ description: '头像', required: false })
  @IsString({ message: 'avatar 类型错误,正确类型 string' })
  @IsOptional()
  readonly avatar?: string
}

实现注册逻辑

user.service.ts写注册的逻辑:

  /** 创建用户 */
  async create(dto: CreateUserDto): Promise<UserEntity> {
    if (dto.password !== dto.confirmPassword)
      throw new ApiException(ApiErrorCode.USER_PASSWORD_INVALID, '两次输入密码不一致,请重试')
    // 防止重复创建 start
    if (await this.findOneByAccount(dto.account))
      throw new ApiException(ApiErrorCode.USER_CREATE_EXISTING, '帐号已存在,请调整后重新注册!')
    if (await this.userRepo.findOne({ where: { phoneNum: dto.phoneNum } }))
      throw new ApiException(ApiErrorCode.USER_CREATE_EXISTING, '当前手机号已存在,请调整后重新注册')
    if (await this.userRepo.findOne({ where: { email: dto.email } }))
      throw new ApiException(ApiErrorCode.USER_CREATE_EXISTING, '当前邮箱已存在,请调整后重新注册')
    // 防止重复创建 end
    const salt = await genSalt()
    dto.password = await hash(dto.password, salt)
    // plainToInstance  忽略转换 @Exclude 装饰器
    const user = plainToInstance(UserEntity, { salt, ...dto }, { ignoreDecorators: true })
    const result = await this.userManager.transaction(async (transactionalEntityManager) => {
      return await transactionalEntityManager.save<UserEntity>(user)
    })
    return result
  }

这里我们对提交的数据做一些判断,并对一些账号已存在的情况抛出具体的异常,来提醒用户。由于我们不能存储密码的明文,这里使用bcryptjs来生成salt,然后对密码进行hash之后,再将salt和hash值同用户信息一起存入数据库。需要安装依赖库:

npm i bcryptjs
npm i --save-dev @types/bcryptjs

测试注册接口

模拟一下注册请求:

发现注册成功了:

返回数据修复

但是这里有个问题,返回的数据里既包含了存入数据库的salt和password,也包含了仅用于数据验证的confirmPassword。我们要将返回类型修改为Partial<UserEntity>,并且利用 class-transformer将salt、password和confirmPassword排除在外,实现修改如下:

  /** 创建用户 */
  async create(dto: CreateUserDto): Promise<Partial<UserEntity>> {
    if (dto.password !== dto.confirmPassword)
      throw new ApiException(ApiErrorCode.USER_PASSWORD_INVALID, '两次输入密码不一致,请重试')
    // 防止重复创建 start
    if (await this.findOneByAccount(dto.account))
      throw new ApiException(ApiErrorCode.USER_CREATE_EXISTING, '帐号已存在,请调整后重新注册!')
    if (await this.userRepo.findOne({ where: { phoneNum: dto.phoneNum } }))
      throw new ApiException(ApiErrorCode.USER_CREATE_EXISTING, '当前手机号已存在,请调整后重新注册')
    if (await this.userRepo.findOne({ where: { email: dto.email } }))
      throw new ApiException(ApiErrorCode.USER_CREATE_EXISTING, '当前邮箱已存在,请调整后重新注册')
    // 防止重复创建 end
    const salt = await genSalt()
    dto.password = await hash(dto.password, salt)
    // plainToInstance  忽略转换 @Exclude 装饰器
    const user = plainToInstance(UserEntity, { salt, ...dto }, { ignoreDecorators: true })
    const result = await this.userManager.transaction(async (transactionalEntityManager) => {
      return await transactionalEntityManager.save<UserEntity>(user)
    })
    //去掉salt,password,confirmPassword
    return plainToInstance(UserEntity, instanceToPlain(result), { excludeExtraneousValues: true })
  }

再请求一下,发现返回接口里不包含敏感信息了:

  • 8
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Nest.js 中实现接口的权限控制可以通过自定义 Guard 实现。Guard 是 Nest.js 中用于控制路由访问权限的模块,它可以在请求路由处理之前对请求进行拦截并进行验证。以下是一个简单的示例: 首先,你需要创建一个 AuthGuard 类,用于对请求进行验证: ```typescript import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common'; import { Observable } from 'rxjs'; @Injectable() export class AuthGuard implements CanActivate { canActivate( context: ExecutionContext, ): boolean | Promise<boolean> | Observable<boolean> { // 在这里进行权限验证,如果通过则返回 true,否则返回 false return true; } } ``` 然后,在需要进行权限控制的路由上使用 AuthGuard: ```typescript import { Controller, Get, UseGuards } from '@nestjs/common'; import { AuthGuard } from './auth.guard'; @Controller('users') export class UsersController { @Get() @UseGuards(AuthGuard) findAll(): string { return 'This action returns all users'; } } ``` 在这个示例中,我们在 `findAll` 方法上使用了 `@UseGuards(AuthGuard)` 装饰器,表示需要通过 AuthGuard 进行权限验证。当请求到达 `findAll` 方法时,Nest.js 会先调用 AuthGuard 的 `canActivate` 方法进行权限验证。如果 `canActivate` 返回 true,则请求会继续被处理;否则,请求将被拒绝。 你可以在 `canActivate` 方法中实现任意的权限验证逻辑,例如验证用户是否拥有访问该接口的权限、验证请求中的 token 是否合法等等。如果验证成功,`canActivate` 方法应该返回 true;否则,返回 false。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值