add EmailVeriyGuard
This commit is contained in:
parent
19452bb01e
commit
03ad1187ce
@ -17,3 +17,17 @@ export class EmailSendDto {
|
||||
@ApiProperty({ enum: EmailScene, enumName: 'EmailScene' })
|
||||
scene: EmailScene
|
||||
}
|
||||
|
||||
export class EmailVerifyDto {
|
||||
@IsNotEmpty()
|
||||
@IsEmail()
|
||||
email: string
|
||||
|
||||
/** @description 发送邮箱接口返回的Token */
|
||||
@IsNotEmpty()
|
||||
token: string
|
||||
|
||||
/** @description 邮箱验证码 */
|
||||
@IsNotEmpty()
|
||||
verifyCode: string
|
||||
}
|
||||
|
50
src/email/email-verify.decorator.ts
Normal file
50
src/email/email-verify.decorator.ts
Normal file
@ -0,0 +1,50 @@
|
||||
import { Reflector } from '@nestjs/core'
|
||||
import {
|
||||
Injectable,
|
||||
CanActivate,
|
||||
ExecutionContext,
|
||||
applyDecorators,
|
||||
UseGuards,
|
||||
SetMetadata,
|
||||
ForbiddenException,
|
||||
} from '@nestjs/common'
|
||||
import { EmailScene, EmailSendDto } from './dto/email.dto'
|
||||
import { EmailService } from './email.service'
|
||||
import { JwtService } from '@nestjs/jwt'
|
||||
import { type Request } from 'express'
|
||||
|
||||
const EMAIL_SCENE_KEY = 'EMAIL_SCENE'
|
||||
|
||||
@Injectable()
|
||||
class EmailVeriyGuard implements CanActivate {
|
||||
constructor(
|
||||
private emailService: EmailService,
|
||||
private reflector: Reflector,
|
||||
private jwtService: JwtService,
|
||||
) {}
|
||||
|
||||
canActivate(ctx: ExecutionContext) {
|
||||
const scene = this.reflector.get<EmailScene>(
|
||||
EMAIL_SCENE_KEY,
|
||||
ctx.getHandler(),
|
||||
)
|
||||
|
||||
const request = ctx.switchToHttp().getRequest<Request>()
|
||||
const { token, email, verifyCode } = request.body
|
||||
|
||||
const payload = this.jwtService.verify<EmailSendDto>(token, {
|
||||
secret: this.emailService.getEmailJwtSecret(verifyCode, scene),
|
||||
})
|
||||
if (payload.email !== email || payload.scene !== scene) {
|
||||
throw new ForbiddenException('请输入正确的邮箱验证码')
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
export function VerifyEmail(scene: EmailScene) {
|
||||
return applyDecorators(
|
||||
SetMetadata(EMAIL_SCENE_KEY, scene),
|
||||
UseGuards(EmailVeriyGuard),
|
||||
)
|
||||
}
|
@ -2,14 +2,13 @@ import {
|
||||
Inject,
|
||||
Injectable,
|
||||
ConflictException,
|
||||
ForbiddenException,
|
||||
NotFoundException,
|
||||
} from '@nestjs/common'
|
||||
import { securityConfig, SecurityConfig } from 'src/common/configs'
|
||||
import { MailerService } from '@nestjs-modules/mailer'
|
||||
import { JwtService } from '@nestjs/jwt'
|
||||
import { PrismaService } from 'nestjs-prisma'
|
||||
import { EmailSendDto, EmailScene } from 'src/email/dto/email.dto'
|
||||
import { EmailScene } from './dto/email.dto'
|
||||
import { UserEntity } from 'src/users/entities/user.entity'
|
||||
|
||||
@Injectable()
|
||||
@ -60,23 +59,7 @@ export class EmailService {
|
||||
return { token, userId: user?.id }
|
||||
}
|
||||
|
||||
// TODO: 做成守卫?
|
||||
async verifyEmail(data: {
|
||||
email: string
|
||||
token: string
|
||||
verifyCode: string
|
||||
scene: EmailScene
|
||||
}) {
|
||||
const { email, token, verifyCode, scene } = data
|
||||
const payload = this.jwtService.verify<EmailSendDto>(token, {
|
||||
secret: this.getEmailJwtSecret(verifyCode, scene),
|
||||
})
|
||||
if (payload.email !== email || payload.scene !== scene) {
|
||||
throw new ForbiddenException('请输入正确的邮箱验证码')
|
||||
}
|
||||
}
|
||||
|
||||
private getEmailJwtSecret(verifyCode: string, scene: EmailScene) {
|
||||
getEmailJwtSecret(verifyCode: string, scene: EmailScene) {
|
||||
return this.secureConfig.jwt_access_secret + verifyCode + scene
|
||||
}
|
||||
|
||||
|
@ -1,13 +1,3 @@
|
||||
import { IsNotEmpty, IsEmail } from 'class-validator'
|
||||
import { EmailVerifyDto } from 'src/email/dto/email.dto'
|
||||
|
||||
export class ChangeEmailDto {
|
||||
@IsNotEmpty()
|
||||
@IsEmail()
|
||||
email: string
|
||||
|
||||
@IsNotEmpty()
|
||||
verifyCode: string
|
||||
|
||||
@IsNotEmpty()
|
||||
token: string
|
||||
}
|
||||
export class ChangeEmailDto extends EmailVerifyDto {}
|
||||
|
@ -2,15 +2,11 @@ import {
|
||||
IsNotEmpty,
|
||||
IsOptional,
|
||||
Length,
|
||||
IsEmail,
|
||||
IsStrongPassword,
|
||||
} from 'class-validator'
|
||||
import { EmailVerifyDto } from 'src/email/dto/email.dto'
|
||||
|
||||
export class CreateUserDto {
|
||||
@IsNotEmpty()
|
||||
@IsEmail()
|
||||
email: string
|
||||
|
||||
export class CreateUserDto extends EmailVerifyDto {
|
||||
@IsOptional()
|
||||
@Length(5, 20)
|
||||
username?: string
|
||||
@ -18,13 +14,4 @@ export class CreateUserDto {
|
||||
@IsNotEmpty()
|
||||
@IsStrongPassword()
|
||||
password: string
|
||||
|
||||
/** @description 验证码 */
|
||||
@IsNotEmpty()
|
||||
@Length(6, 6)
|
||||
verifyCode: string
|
||||
|
||||
/** @description 发送邮箱接口返回的Token */
|
||||
@IsNotEmpty()
|
||||
token: string
|
||||
}
|
||||
|
@ -1,16 +1,7 @@
|
||||
import { IsEmail, IsNotEmpty, IsStrongPassword } from 'class-validator'
|
||||
|
||||
export class DeleteUserDto {
|
||||
@IsNotEmpty()
|
||||
@IsEmail()
|
||||
email: string
|
||||
|
||||
@IsNotEmpty()
|
||||
verifyCode: string
|
||||
|
||||
@IsNotEmpty()
|
||||
token: string
|
||||
import { IsNotEmpty, IsStrongPassword } from 'class-validator'
|
||||
import { EmailVerifyDto } from 'src/email/dto/email.dto'
|
||||
|
||||
export class DeleteUserDto extends EmailVerifyDto {
|
||||
@IsNotEmpty()
|
||||
@IsStrongPassword()
|
||||
password: string
|
||||
|
@ -1,16 +1,7 @@
|
||||
import { IsEmail, IsNotEmpty, IsStrongPassword } from 'class-validator'
|
||||
|
||||
export class ResetPassword {
|
||||
@IsNotEmpty()
|
||||
@IsEmail()
|
||||
email: string
|
||||
|
||||
@IsNotEmpty()
|
||||
verifyCode: string
|
||||
|
||||
@IsNotEmpty()
|
||||
token: string
|
||||
import { IsNotEmpty, IsStrongPassword } from 'class-validator'
|
||||
import { EmailVerifyDto } from 'src/email/dto/email.dto'
|
||||
|
||||
export class ResetPassword extends EmailVerifyDto {
|
||||
@IsNotEmpty()
|
||||
@IsStrongPassword()
|
||||
password: string
|
||||
|
@ -87,7 +87,6 @@ export class MeController {
|
||||
@Body() payload: ChangeEmailDto,
|
||||
@User('userId') userId: string,
|
||||
): Promise<UserEntity> {
|
||||
console.log(query)
|
||||
return this.meService.changeEmail(payload, userId)
|
||||
}
|
||||
|
||||
|
@ -9,29 +9,23 @@ import { PrismaService } from 'nestjs-prisma'
|
||||
import { JwtService } from '@nestjs/jwt'
|
||||
import { securityConfig, SecurityConfig } from 'src/common/configs'
|
||||
import { EmailScene } from 'src/email/dto/email.dto'
|
||||
import { EmailService } from 'src/email/email.service'
|
||||
import { DeleteUserDto } from './dto/delete-user.dto'
|
||||
import { ChangePassword } from './dto/change-password.dto'
|
||||
import { TokenContnet } from './dto/token.dto'
|
||||
import { ChangeEmailDto } from './dto/change-email.dto'
|
||||
import { VerifyEmail } from 'src/email/email-verify.decorator'
|
||||
|
||||
@Injectable()
|
||||
export class MeService {
|
||||
constructor(
|
||||
private jwtService: JwtService,
|
||||
private prismaService: PrismaService,
|
||||
private emailService: EmailService,
|
||||
@Inject(securityConfig.KEY) private secureConfig: SecurityConfig,
|
||||
) {}
|
||||
|
||||
@VerifyEmail(EmailScene.deleteUser)
|
||||
async deleteUser(userToDelete: DeleteUserDto, userId: string) {
|
||||
const { email, token, verifyCode, password } = userToDelete
|
||||
await this.emailService.verifyEmail({
|
||||
email,
|
||||
token,
|
||||
verifyCode,
|
||||
scene: EmailScene.deleteUser,
|
||||
})
|
||||
const { password } = userToDelete
|
||||
const user = await this.prismaService.user.findUniqueOrThrow({
|
||||
where: { email: userToDelete.email },
|
||||
})
|
||||
@ -63,14 +57,9 @@ export class MeService {
|
||||
})
|
||||
}
|
||||
|
||||
@VerifyEmail(EmailScene.changeEmail)
|
||||
async changeEmail(payload: ChangeEmailDto, userId: string) {
|
||||
const { email, token, verifyCode } = payload
|
||||
await this.emailService.verifyEmail({
|
||||
email,
|
||||
token,
|
||||
verifyCode,
|
||||
scene: EmailScene.changeEmail,
|
||||
})
|
||||
const { email } = payload
|
||||
return this.prismaService.user.update({
|
||||
where: { id: userId },
|
||||
data: { email },
|
||||
|
@ -8,6 +8,7 @@ import { EmailScene } from 'src/email/dto/email.dto'
|
||||
import { EmailService } from 'src/email/email.service'
|
||||
import { ResetPassword } from './dto/reset-password.dto'
|
||||
import { Token, TokenPayload } from './dto/token.dto'
|
||||
import { VerifyEmail } from 'src/email/email-verify.decorator'
|
||||
|
||||
@Injectable()
|
||||
export class UsersService {
|
||||
@ -18,14 +19,9 @@ export class UsersService {
|
||||
@Inject(securityConfig.KEY) private secureConfig: SecurityConfig,
|
||||
) {}
|
||||
|
||||
@VerifyEmail(EmailScene.register)
|
||||
async registerByEmail(userToCreate: CreateUserDto) {
|
||||
const { email, token, verifyCode, username, password } = userToCreate
|
||||
await this.emailService.verifyEmail({
|
||||
email,
|
||||
token,
|
||||
verifyCode,
|
||||
scene: EmailScene.register,
|
||||
})
|
||||
const { email, username, password } = userToCreate
|
||||
const hashedPassword = await bcrypt.hash(
|
||||
password,
|
||||
this.secureConfig.bcryptSaltOrRound,
|
||||
@ -50,14 +46,9 @@ export class UsersService {
|
||||
return this.generateTokens({ userId: user.id })
|
||||
}
|
||||
|
||||
@VerifyEmail(EmailScene.forgetPassword)
|
||||
async resetPasswordByEmail(data: ResetPassword, userId: string) {
|
||||
const { email, token, verifyCode, password } = data
|
||||
await this.emailService.verifyEmail({
|
||||
email,
|
||||
token,
|
||||
verifyCode,
|
||||
scene: EmailScene.forgetPassword,
|
||||
})
|
||||
const { password } = data
|
||||
const hashedPassword = await bcrypt.hash(
|
||||
password,
|
||||
this.secureConfig.bcryptSaltOrRound,
|
||||
|
Loading…
Reference in New Issue
Block a user