NestJS简明教程——安全

登录和注册(代码示例)

需要安装的依赖

bash 复制代码
pnpm add @nestjs/jwt @nestjs/throttler helmet  

auth.service.ts

ts 复制代码
import {
  ConflictException,
  Injectable,
  UnauthorizedException,
} from '@nestjs/common';
import { UsersService } from '../users/users.service';
import * as bcrypt from 'bcrypt';
import { SignUpDto } from './dto/sign-up.dto';
import { SignInDto } from './dto/sign-in.dto';
import { NotFoundError } from '@mikro-orm/core';
import { JwtService } from '@nestjs/jwt';

@Injectable()
export class AuthService {
  constructor(
    private readonly usersService: UsersService,
    private readonly jwtService: JwtService,
  ) {}

  async generateToken(user: any): Promise<{ access_token: string }> {
    const payload = { email: user.email, sub: user.id };
    return {
      access_token: await this.jwtService.signAsync(payload),
    };
  }

  async signUp(signUpDto: SignUpDto): Promise<any> {
    const { email, name, password } = signUpDto;

    const user = await this.usersService.findOne(email);
    if (user) {
      throw new ConflictException('User already exists');
    }

    const hashedPassword = await bcrypt.hash(password, 10);

    await this.usersService.create({ email, name, password: hashedPassword });

    return await this.usersService.findOne(email);
  }

  async signIn(signInDto: SignInDto): Promise<any> {
    const { email, password } = signInDto;

    const user = await this.usersService.findOne(email);
    if (!user) {
      throw new NotFoundError('User not found');
    }

    const isPasswordValid = await bcrypt.compare(password, user.password);
    if (!isPasswordValid) {
      throw new UnauthorizedException('Invalid password');
    }

    const token = await this.generateToken(user);

    return { user, token };
  }
}

auth.controller.ts

ts 复制代码
import { Controller } from '@nestjs/common';
import { AuthService } from './auth.service';
import { Post, Body } from '@nestjs/common';
import { SignUpDto } from './dto/sign-up.dto';
import { SignInDto } from './dto/sign-in.dto';
import { Public } from './decorator/public.decorator';

@Controller('auth')
export class AuthController {
  constructor(private readonly authService: AuthService) {}

  @Post('register')
  @Public()
  async signUp(@Body() signUpDto: SignUpDto): Promise<any> {
    return this.authService.signUp(signUpDto);
  }

  @Post('login')
  @Public()
  async signIn(@Body() signInDto: SignInDto): Promise<any> {
    return this.authService.signIn(signInDto);
  }

}

auth.module.ts

ts 复制代码
import { Module } from '@nestjs/common';
import { AuthService } from './auth.service';
import { AuthController } from './auth.controller';
import { UsersModule } from '../users/users.module';
import { JwtModule } from '@nestjs/jwt';
import { APP_GUARD } from '@nestjs/core';
import { AuthGuard } from './auth.guard';

@Module({
  imports: [
    UsersModule,
    JwtModule.register({
      secret: process.env.JWT_SECRET,
      signOptions: { expiresIn: '1h', algorithm: 'HS256' },
    }),
  ],
  providers: [
    AuthService,
    {
      provide: APP_GUARD,
      useClass: AuthGuard,
    },
  ],
  controllers: [AuthController],
})
export class AuthModule {}

JWT

auth.guard.ts

bash 复制代码
nest g guard auth/auth --no-spec --flat
ts 复制代码
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
import { UnauthorizedException } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { Reflector } from '@nestjs/core';
import { IS_PUBLIC_KEY } from './decorator/public.decorator';

@Injectable()
export class AuthGuard implements CanActivate {
  constructor(
    private readonly jwtService: JwtService,
    private reflector: Reflector,
  ) {}

  private extractTokenFromHeader(request: any): string | undefined {
    const [type, token] = request.headers.authorization?.split(' ') ?? [];
    return type === 'Bearer' ? token : undefined;
  }

  async canActivate(context: ExecutionContext): Promise<boolean> {

    const isPublic = this.reflector.getAllAndOverride<boolean>(IS_PUBLIC_KEY, [
      context.getHandler(),
    ]);
    if (isPublic) {
      // 💡 See this condition
      return true;
    }

    // get the request object from the execution context
    const request = context.switchToHttp().getRequest();
    const token = this.extractTokenFromHeader(request);

    if (!token) {
      throw new UnauthorizedException('No token provided');
    }

    // verify the token using the JwtService
    try {
      const payload = await this.jwtService.verifyAsync(token);
      request['user'] = payload;
    } catch (error) {
      throw new UnauthorizedException('Invalid token');
    }

    return true;
  }
}

public.decorator.ts

bash 复制代码
nest g decorator auth/decorator/public --flat
ts 复制代码
import { SetMetadata } from '@nestjs/common';

export const IS_PUBLIC_KEY = 'isPublic';
export const Public = () => SetMetadata(IS_PUBLIC_KEY, true);

限流

app.module.ts

ts 复制代码
import { ThrottlerModule } from '@nestjs/throttler';
import throttlerConfig from './common/config/c';
ThrottlerModule.forRoot(throttlerConfig)

throttler.config.ts

复制代码
export default [
  {
    name: 'short',
    ttl: 1000,
    limit: 3,
  },
  {
    name: 'medium',
    ttl: 10000,
    limit: 20,
  },
  {
    name: 'long',
    ttl: 60000,
    limit: 100,
  },
];

其他

helmet

main.ts

ts 复制代码
// 加入 Helmet 中间件以增强安全性
  app.use(helmet());
相关推荐
许彰午2 小时前
政务督办的分合模式:主办协办的并发审批
前端·javascript·政务
易筋紫容2 小时前
创建型模式:对象的诞生艺术
开发语言·前端·javascript
888CC++2 小时前
C++ 快速学习指南:从入门到进阶的实战路线
开发语言·c++
小小晓.2 小时前
C++记:函数
开发语言·c++·算法
牡丹雅忻13 小时前
AES 加密模式演进:从 ECB、CBC 到 GCM 的 C# 深度实践
java·开发语言·c#
连续讨伐3 小时前
php小结
开发语言·php
RisunJan3 小时前
Linux命令-shutdown(安全关闭或重启系统)
linux·服务器·安全
进击的程序猿~3 小时前
Go 并发底层原理面试学习指南
开发语言·面试·golang
脚踏实地皮皮晨3 小时前
002002002_DepandencyObject类2
开发语言·windows·算法·c#·visual studio
练习时长一年3 小时前
@SneakyThrows注解
开发语言