08-nestjs基础实践,密码加密,序列化拦截器,数据脱敏,

密码加密

安装加密插件:

sh 复制代码
$ npm i argon2

加密注册用户的密码, 编辑src/user/user.service.ts

javascript 复制代码
  import * as argon2 from 'argon2'
  ...
  // 创建数据
  async create(dto: Partial<User>) {
    ...
    // 使用argon2对密码进行加密
    + user.password = await argon2.hash(user.password)
    ...
  }

用户登陆时,需要验证用户密码是否正确,编辑src/auth/auth.service.ts

ts 复制代码
...
import * as argon2 from 'argon2'

@Injectable()
export class AuthService {
  constructor(
    private userService: UserService,
    private jwt: JwtService,
  ) {}

  // 用户登录
  async singin(username: string, password: string) {
    const res = await this.userService.findAll({ username } as getUserDto)
    const user = res.total > 0 ? res.records[0] : null
    if (!user) throw new ForbiddenException('用户不存在,请先注册')
    const { password: userPassword, ...userInfo } = user
    // 比较用户密码
    + nst isValidPassword = await argon2.verify(userPassword, password)
    + if (!isValidPassword) throw new ForbiddenException('用户名或密码错误')
    // 生成token
    const token = await this.jwt.signAsync({ username, sub: user.id })
    return { token, userInfo }
  }

  // 用户注册
  async signup(username: string, password: string) {
    + nst users = await this.userService.findAll({ username } as getUserDto)
    + const user = users.total > 0 ? users.records[0] : null
    + if (user) throw new ForbiddenException('用户已存在,不能重复注册')
    const saverResult = await this.userService.create({ username, password })
    return saverResult
  }
}

拦截器

创建拦截器

sh 复制代码
$ nest g itc interceptors/serialize --no-spec --flat -d
$ nest g itc interceptors/serialize --no-spec --flat

生成文件src/interceptors/serialize.interceptors.ts

ts 复制代码
import { CallHandler, ExecutionContext, Injectable, NestInterceptor } from '@nestjs/common'
import { Observable, map } from 'rxjs'
/** 自定义序列化拦截器 */
@Injectable()
export class SerializeInterceptor implements NestInterceptor {
  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    // 拦截器之前执行
    console.log('在拦截器之前执行', context.switchToHttp().getRequest().user)
    return next.handle().pipe(
      map((data) => {
        console.log('在拦截器之后执行', context.switchToHttp().getRequest().user)
        return data
      }),
    )
  }
}

在控制器中使用

ts 复制代码
...
@Get(':id')
@UseInterceptors(SerializeInterceptor)
getUserById(@Param('id') id: number) {
  return this.userService.findOne(id)
}
...

序列换拦截器

编辑src/user/user.entity.ts

ts 复制代码
  ...
  @Column()
  @Exclude() // 添加Exclude属性,此属性不会返回给请求用户
  password: string
  ...

修改user控制器

less 复制代码
import { ClassSerializerInterceptor } from '@nestjs/common'
...
@Get(':id')
+ seInterceptors(ClassSerializerInterceptor)
getUserById(@Param('id') id: number) {
  return this.userService.findOne(id)
}
...

请求接口如下

利用自定义dto序列化拦截器

编辑文件src/interceptors/serialize.interceptors.ts

ts 复制代码
import { CallHandler, ExecutionContext, Injectable, NestInterceptor } from '@nestjs/common'
import { plainToInstance } from 'class-transformer'
import { Observable, map } from 'rxjs'

export interface ClassConstructor {
  new (...arg: any[]): {}
}
/**
 * 自定义序列化拦截器
 * 用法如下
 * @UseInterceptors(new SerializeInterceptor(dto))
 * @Get('/:id')
 * async findUser(@Param('id') id: string) { }
 * 
 * @param dto 用于定义输出响应的类,如user.dto.ts
 * @returns
 */
@Injectable()
export class SerializeInterceptor implements NestInterceptor {
  constructor(private dto: ClassConstructor) {}
  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    // 拦截器之前执行 ,console.log('在拦截器之前执行', context.switchToHttp().getRequest().user)
    return next.handle().pipe(
      map((data) => {
        // console.log('在拦截器之后执行')
        return plainToInstance(this.dto, data, {
          // 设置为true, 所有经过该拦截器的数据都需要设置Expose(暴露的意思)或者Exclude(排除的意思)
          excludeExtraneousValues: true,
        })
      }),
    )
  }
}

创建自定义装饰器,简化序列化拦截器的用法,创建文件src\decorators\serialize.decorators.ts

ts 复制代码
import { UseInterceptors } from '@nestjs/common'
import { ClassConstructor, SerializeInterceptor } from '../interceptors/serialize.interceptor'
/**
 * 包装为装饰器,简化自定义序列化拦截器的使用
 * @param dto 用于定义输出响应的类,如user.dto.ts
 *
 * 用于控制器
 * @Controller('auth')
 * @Serialize(UserDto)
 * export class UsersController {}
 *
 * 用于路由
 * @Serialize(UserDto)
 * @Get('/:id')
 * async findUser(@Param('id') id: string) { }
 *
 * @returns
 */
export function Serialize(dto: ClassConstructor) {
  return UseInterceptors(new SerializeInterceptor(dto))
}

创建UserDto文件src/user/dto/user.dto.ts

ts 复制代码
import { Expose, Transform } from 'class-transformer'
import { Profile } from 'passport'
import { Logs } from '../../logs/logs.entity'
import { Roles } from '../../roles/roles.entity'

// 用于定义用户的响应字段
export class UserDto {
  @Expose()
  id: number
  @Expose()
  username: string

  @Transform(({ obj }) => obj.logs)
  @Expose()
  logs: Logs[]

  @Transform(({ obj }) => obj.roles)
  @Expose()
  roles: Roles[]

  @Transform(({ obj }) => obj.profile)
  @Expose()
  profile: Profile
}

在控制器的方法中使用

ts 复制代码
import { Serialize } from '../decorators/serialize.decorators'
import { UserDto } from './dto/user.dto'

@Get(':id')
@Serialize(UserDto)
getUserById(@Param('id') id: number) {
  return this.userService.findOne(id)
}
相关推荐
小丁爱养花2 分钟前
前端三剑客(三):JavaScript
开发语言·前端·javascript
ZwaterZ10 分钟前
vue el-table表格点击某行触发事件&&操作栏点击和row-click冲突问题
前端·vue.js·elementui·c#·vue
西凉河的葛三叔15 分钟前
vue3+elementui-plus el-dialog全局配置点击空白处不关闭弹窗
前端·vue3·elementui-plus
周三有雨22 分钟前
【面试题系列Vue07】Vuex是什么?使用Vuex的好处有哪些?
前端·vue.js·面试·typescript
木古古1835 分钟前
使用chrome 访问虚拟机Apache2 的默认页面,出现了ERR_ADDRESS_UNREACHABLE这个鸟问题
前端·chrome·apache
爱米的前端小笔记44 分钟前
前端八股自学笔记分享—页面布局(二)
前端·笔记·学习·面试·求职招聘
loey_ln1 小时前
webpack配置和打包性能优化
前端·webpack·性能优化
建群新人小猿1 小时前
会员等级经验问题
android·开发语言·前端·javascript·php
爱上语文1 小时前
HTML和CSS 表单、表格练习
前端·css·html