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());
相关推荐
落魄实习生15 小时前
Agent Scope Java 2.x 系列【7】工具使用
java·开发语言·ai
茉莉玫瑰花茶15 小时前
GO [ 方法 ]
开发语言·后端·golang
狗凯之家源码网16 小时前
微博图床 PHP 系统搭建与实战应用指南
android·开发语言·php
wengad16 小时前
从 wangEditor 迁到 wangEditor-next
前端·javascript·vue.js
泛联新安16 小时前
软件定义汽车时代,如何让AI研发“可信”?——泛联新安构建汽车企业级可信AI体系的落地路径
大数据·人工智能·安全·网络安全·汽车·漏洞挖掘·代码安全
域智盾软件分享17 小时前
电脑文件加密软件实战部署:从试点到全业务落地完整教程
其他·安全
乌暮17 小时前
深入理解 Java 泛型:把「万能盒子」用对、用稳
java·开发语言·后端·学习
行者-全栈开发17 小时前
【前端/JS框架】CVE-2026-3854:GitHub Enterprise Server X-Stat注入XSS漏洞深度剖析与紧急修复指南
前端·javascript·github
2601_9622186118 小时前
C语言中三种库的编译和使用方法
开发语言·c++
传奇开心果编程18 小时前
中庸、有容、破执:仓颉编程语言的中华智慧结晶——三重哲学境界思辨
开发语言·华为·开源·软件工程