密码加密
安装加密插件:
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)
}