diff --git a/package.json b/package.json index 7722530..55dac81 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "@nestjs/platform-express": "^9.0.0", "@nestjs/swagger": "^6.2.1", "@prisma/client": "^4.10.1", + "@types/jsonwebtoken": "^9.0.1", "bcrypt": "^5.1.0", "class-transformer": "^0.5.1", "class-validator": "^0.14.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9fd9b12..6011444 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -15,6 +15,7 @@ specifiers: '@prisma/client': ^4.10.1 '@types/bcrypt': ^5.0.0 '@types/express': ^4.17.13 + '@types/jsonwebtoken': ^9.0.1 '@types/node': 18.11.18 '@types/passport-jwt': ^3.0.8 '@typescript-eslint/eslint-plugin': ^5.0.0 @@ -53,6 +54,7 @@ dependencies: '@nestjs/platform-express': 9.3.9_77foi4w27ghy47yutmnzv7krjy '@nestjs/swagger': 6.2.1_ldzmua2hsw2tga2e42i6lmmdty '@prisma/client': 4.10.1_prisma@4.10.1 + '@types/jsonwebtoken': 9.0.1 bcrypt: 5.1.0 class-transformer: 0.5.1 class-validator: 0.14.0 diff --git a/src/auth/auth.module.ts b/src/auth/auth.module.ts index 0edb061..db7f052 100644 --- a/src/auth/auth.module.ts +++ b/src/auth/auth.module.ts @@ -4,9 +4,16 @@ import { AuthController } from './auth.controller' import { PasswordService } from './password.service' import { JwtService } from '@nestjs/jwt' import { JwtStrategy } from './strategies/jwt.strategy' +import { EmailService } from 'src/email/email.service' @Module({ controllers: [AuthController], - providers: [AuthService, JwtService, JwtStrategy, PasswordService], + providers: [ + AuthService, + JwtService, + JwtStrategy, + PasswordService, + EmailService, + ], }) export class AuthModule {} diff --git a/src/auth/auth.service.ts b/src/auth/auth.service.ts index f62c789..30d02a9 100644 --- a/src/auth/auth.service.ts +++ b/src/auth/auth.service.ts @@ -10,7 +10,8 @@ import { Token, TokenPayload } from './dto/token.dto' import { JwtService } from '@nestjs/jwt' import { securityConfig, SecurityConfig } from 'src/common/configs' import { CreateUserDto } from 'src/users/dto/create-user.dto' -import { EmailSendDto } from 'src/email/dto/email.dto' +import { EmailSendDto, EmailScene } from 'src/email/dto/email.dto' +import { EmailService } from 'src/email/email.service' @Injectable() export class AuthService { @@ -18,32 +19,28 @@ export class AuthService { private passwordService: PasswordService, private jwtService: JwtService, private prismaService: PrismaService, + private emailService: EmailService, @Inject(securityConfig.KEY) private secureConfig: SecurityConfig, ) {} async register(userToCreate: CreateUserDto) { - console.log(userToCreate) - try { - const tokenPayload = this.jwtService.verify( - userToCreate.token, - { - secret: - this.secureConfig.jwt_access_secret + - userToCreate.verificationCode + - 'register', - }, - ) - if ( - tokenPayload.email !== userToCreate.email || - tokenPayload.scene !== 'register' - ) { - throw new ForbiddenException('请输入正确的邮箱') - } - } catch (err) { - console.error(err) - throw new ForbiddenException(err.message) + const tokenPayload = this.jwtService.verify( + userToCreate.token, + { + secret: this.emailService.getEmailJwtSecret( + userToCreate.verificationCode, + EmailScene.register, + ), + }, + ) + if ( + tokenPayload.email !== userToCreate.email || + tokenPayload.scene !== 'register' + ) { + throw new ForbiddenException('请输入正确的邮箱') } + const hashedPassword = await this.passwordService.hashPassword( userToCreate.password, ) diff --git a/src/common/filters/jwt-exceptions.filter.ts b/src/common/filters/jwt-exceptions.filter.ts new file mode 100644 index 0000000..3df38ff --- /dev/null +++ b/src/common/filters/jwt-exceptions.filter.ts @@ -0,0 +1,18 @@ +import { + Catch, + Logger, + ArgumentsHost, + ForbiddenException, +} from '@nestjs/common' +import { BaseExceptionFilter } from '@nestjs/core' +import { JsonWebTokenError } from 'jsonwebtoken' + +@Catch(JsonWebTokenError) +export class JwtExceptionsFilter extends BaseExceptionFilter { + private readonly logger = new Logger(JsonWebTokenError.name) + + catch(exception: JsonWebTokenError, host: ArgumentsHost) { + this.logger.error(exception) + super.catch(new ForbiddenException(exception.message), host) + } +} diff --git a/src/email/email.module.ts b/src/email/email.module.ts index 439dc1d..0ca6c8b 100644 --- a/src/email/email.module.ts +++ b/src/email/email.module.ts @@ -33,5 +33,6 @@ import { HandlebarsAdapter } from '@nestjs-modules/mailer/dist/adapters/handleba ], controllers: [EmailController], providers: [EmailService, JwtService], + exports: [EmailService], }) export class EmailModule {} diff --git a/src/email/email.service.ts b/src/email/email.service.ts index 4c36d2b..458d20b 100644 --- a/src/email/email.service.ts +++ b/src/email/email.service.ts @@ -36,20 +36,21 @@ export class EmailService { const verificationCode = this.generateRandomNum() const registerToken = this.jwtService.sign( { email, scene }, - { - secret: this.secureConfig.jwt_access_secret + verificationCode + scene, - expiresIn: '30min', - }, + { secret: this.getEmailJwtSecret(verificationCode, scene) }, ) await this.mailerService.sendMail({ to: email, subject: '注册qiuxu.site', html: `您正在注册qiuxu.site,验证码为 ${verificationCode},30分钟内有效`, }) - return { registerToken, verificationCode } + return registerToken + } + + getEmailJwtSecret(verificationCode: string, scene: EmailScene) { + return this.secureConfig.jwt_access_secret + verificationCode + scene } private generateRandomNum() { - return Math.floor(Math.random() * 899999 + 100000) + return Math.floor(Math.random() * 899999 + 100000).toString() } } diff --git a/src/main.ts b/src/main.ts index 8f1ccad..6c5b1de 100644 --- a/src/main.ts +++ b/src/main.ts @@ -3,6 +3,7 @@ import { ValidationPipe } from '@nestjs/common' import { ConfigService } from '@nestjs/config' import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger' import { PrismaClientExceptionFilter, PrismaService } from 'nestjs-prisma' +import { JwtExceptionsFilter } from './common/filters/jwt-exceptions.filter' import { AppModule } from './app.module' async function bootstrap() { @@ -18,6 +19,7 @@ async function bootstrap() { // Prisma Client Exception Filter for unhandled exceptions const { httpAdapter } = app.get(HttpAdapterHost) app.useGlobalFilters(new PrismaClientExceptionFilter(httpAdapter)) + app.useGlobalFilters(new JwtExceptionsFilter(httpAdapter)) // Swagger Api const options = new DocumentBuilder()