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());
相关推荐
小白学大数据28 分钟前
Codex 里的 GPT 6 Astra、GPT 5.6 Sol、Terra、Luna 怎么选
开发语言·gpt·microsoft
moonsims44 分钟前
Voliro 无人机-Aerial Mobile Robot(空中移动机器人):把无人机从“飞过去拍摄”,升级成“飞过去并与目标物理接触、测量甚至操作”
前端·人工智能·安全·无人机·量子计算
l1t2 小时前
DeepSeek 4.1总结的Tom Lane 谈塑造 Postgres 三十年历程的架构决策
开发语言·数据库·postgresql·架构
变与不变8062 小时前
Debug 调试与排错规范
前端·javascript
djarmy8 小时前
MAIN.c(1): warning C318: can’t open file ‘STC8G.H’ 报错 解决 分析
c语言·开发语言·mongodb
HEJOO98 小时前
深入理解 Java volatile 关键字:原理、用法与常见误区
java·开发语言·spring
曹牧8 小时前
Java:no content to map due to end of input
java·开发语言
梦梦代码精8 小时前
《回收租赁系统技术选型避坑指南:业务闭环与二开自由度详解》
开发语言·低代码·docker·开源·代码规范
隔窗听雨眠9 小时前
记一次SQL Server数据库性能分析:从CPU100%到单配置修复的完整诊断
开发语言·数据库·php
David猪大卫9 小时前
【C++修炼】智能指针使用及原理
开发语言·c++·经验分享·笔记·学习·考研·面试