首頁 >web前端 >js教程 >在 NestJS 中使用 Passport 實作 JWT 驗證日(第 1 部分)

在 NestJS 中使用 Passport 實作 JWT 驗證日(第 1 部分)

Linda Hamilton
Linda Hamilton原創
2025-01-01 10:25:10201瀏覽

第一步

nest g 資源授權

這將進一步要求您進行選擇
❯ REST API
GraphQL(代碼優先)
GraphQL(模式優先)
微服務(非 HTTP)
WebSocket

選擇 REST API,這將為您產生具有 dtos 服務控制器和模組的整個模組

註冊用戶

由於我們實施基於電子郵件/密碼的身份驗證作為第一步,我們將註冊使用者。

  1. 首先驗證以確保資料合法,並添加密碼強度驗證以減輕暴力攻擊。
  2. 之後進行消毒,以確保資料可以安全使用。
  3. 檢查資料庫中是否已存在該使用者記錄,如果存在,則表示該使用者已經擁有帳戶,因此請發送此電子郵件已註冊的回覆。
  4. 如果上述檢查失敗,則表示我們需要註冊一個用戶 取得使用者密碼並使用良好的雜湊庫(如 bcrypt 或 argon2
  5. )對其進行雜湊處理
  6. 散列後將使用者記錄插入資料庫。
  7. 向使用者發送電子郵件以驗證電子郵件是否合法。
  8. 為路由新增速率限制以避免 DDoS 攻擊

1 驗證傳入數據

由於 Nest js 與類驗證器等推薦的驗證包有很強的集成,但根據我之前的經驗,我在 React JS 前端使用 zod 進行驗證,所以我發現了一個很棒的
Nest js 生態系統的解決方案稱為 Nests Zod,所以我現在更喜歡使用這個。首先安裝庫
npm 我 Nestjs-zod

import { createZodDto } from 'nestjs-zod';
import { z } from 'zod';
const passwordStrengthRegex =
  /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/;
const registerUserSchema = z
  .object({
    email: z.string().email(),
    password: z
      .string()
      .min(8)
      .regex(
        passwordStrengthRegex,
        'Password must contain at least one uppercase letter, one lowercase letter, one number, and one special character',
      ),
    confirmPassword: z.string().min(8),
  })
  .refine((data) => data.password === data.confirmPassword, {
    message: 'Passwords do not match',
  });

export class RegisterUserDto extends createZodDto(registerUserSchema) {}

然後在路線上應用驗證管道

import { Controller, Post, Body, Version, UsePipes } from '@nestjs/common';
import { AuthService } from './auth.service';
import { RegisterUserDto } from './dto/register.dto';
import { ZodValidationPipe } from 'nestjs-zod';

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

  @Version('1')
  @Post()
  @UsePipes(ZodValidationPipe)
  async registerUser(@Body() registerUserDto: RegisterUserDto) {
    return await this.authService.registerUser(registerUserDto);
  }
}

如果我們提供的所有輸入都正確

Day  Implementing JWT Authentication in NestJS with Passport (Part 1)

這樣我們就完成了第一步

讓我們清理數據

我們有三個輸入

  • 密碼:通常密碼不應該被清理,因為它們永遠不會發送並顯示到前端,即使有人向密碼發送惡意腳本,最終它也會被散列,不需要
  • 確認密碼:與上面的故事相同
  • 電子郵件:是的,電子郵件會傳送並呈現給客戶端,因此電子郵件欄位必須進行清理以減輕注入和腳本攻擊

但是我們明確地添加了電子郵件:z.string().email(),這對於這個用例來說已經足夠了
Day  Implementing JWT Authentication in NestJS with Passport (Part 1)

但是為了增加額外的安全性,我們可以加入一個消毒層

import { createZodDto } from 'nestjs-zod';
import { z } from 'zod';
import * as xss from 'xss'; 

const passwordStrengthRegex =
  /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/;

const registerUserSchema = z
  .object({
    email: z.string().transform((input) => xss.filterXSS(input)), // Sanitizing input using xss
    password: z
      .string()
      .min(8)
      .regex(
        passwordStrengthRegex,
        'Password must contain at least one uppercase letter, one lowercase letter, one number, and one special character',
      ),
    confirmPassword: z.string().min(8),
  })
  .refine((data) => data.password === data.confirmPassword, {
    message: 'Passwords do not match',
  });

export class RegisterUserDto extends createZodDto(registerUserSchema) {}

Day  Implementing JWT Authentication in NestJS with Passport (Part 1)

這是我們再次加入的測試

電子郵件:z
.string()
.email()

步驟3,4,5

import { createZodDto } from 'nestjs-zod';
import { z } from 'zod';
const passwordStrengthRegex =
  /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/;
const registerUserSchema = z
  .object({
    email: z.string().email(),
    password: z
      .string()
      .min(8)
      .regex(
        passwordStrengthRegex,
        'Password must contain at least one uppercase letter, one lowercase letter, one number, and one special character',
      ),
    confirmPassword: z.string().min(8),
  })
  .refine((data) => data.password === data.confirmPassword, {
    message: 'Passwords do not match',
  });

export class RegisterUserDto extends createZodDto(registerUserSchema) {}

需要注意的要點我剛剛回傳了一條成功訊息,沒有相關數據
像 ID 或電子郵件一樣向使用者傳送,因為在此步驟中不需要向使用者傳送回資料。註冊後,用戶將被重定向到登錄頁面以填寫詳細信息,因此避免發送不必要的數據是一個很好的安全實踐

Day  Implementing JWT Authentication in NestJS with Passport (Part 1)

速率限制

在nestjs中實現速率限制非常簡單,只需安裝nestjs/throttler並進行全域配置即可。
要安裝套件,請執行 npm i --save @nestjs/throttler

import { Controller, Post, Body, Version, UsePipes } from '@nestjs/common';
import { AuthService } from './auth.service';
import { RegisterUserDto } from './dto/register.dto';
import { ZodValidationPipe } from 'nestjs-zod';

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

  @Version('1')
  @Post()
  @UsePipes(ZodValidationPipe)
  async registerUser(@Body() registerUserDto: RegisterUserDto) {
    return await this.authService.registerUser(registerUserDto);
  }
}

然後加入nestjsthrottleguard作為全域守衛

import { createZodDto } from 'nestjs-zod';
import { z } from 'zod';
import * as xss from 'xss'; 

const passwordStrengthRegex =
  /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/;

const registerUserSchema = z
  .object({
    email: z.string().transform((input) => xss.filterXSS(input)), // Sanitizing input using xss
    password: z
      .string()
      .min(8)
      .regex(
        passwordStrengthRegex,
        'Password must contain at least one uppercase letter, one lowercase letter, one number, and one special character',
      ),
    confirmPassword: z.string().min(8),
  })
  .refine((data) => data.password === data.confirmPassword, {
    message: 'Passwords do not match',
  });

export class RegisterUserDto extends createZodDto(registerUserSchema) {}

就是這裡

import {
  BadRequestException,
  Injectable,
  InternalServerErrorException,
} from '@nestjs/common';
import { RegisterUserDto } from './dto/register.dto';
import { PrismaService } from 'src/prismaModule/prisma.service';
import * as argon2 from 'argon2';

@Injectable()
export class AuthService {
  constructor(private readonly prismaService: PrismaService) {}
  async registerUser(registerUserDto: RegisterUserDto) {
    // data is validate and sanitized by the registerUserDto
    const { email, password } = registerUserDto;

    try {
      // check if user already exists
      const user = await this.prismaService.user.findFirst({
        where: {
          email,
        },
      });

      if (user) {
        throw new BadRequestException('user already eists ');
      }
      //if use not exists lets hash user password
      const hashedPassword = await argon2.hash(registerUserDto.password);

      // time to create user
      const userData = await this.prismaService.user.create({
        data: {
          email,
          password: hashedPassword,
        },
      });

      if (!userData) {
        throw new InternalServerErrorException(
          'some thing went wrong while registring user',
        );
      }

      // if user is created successfully then  send email to user for email varification
      return {
        success: true,
        message: 'user created successfully',
      };
    } catch (error) {
      throw error;
    }
  }
}

因為註冊用戶端點是一個敏感端點暴力破解
否則可能會發生字典攻擊,我們嚴格限制速率

發送驗證郵件

用於向使用者發送驗證電子郵件的使用 Resend 是一項非常棒的易於使用的服務。但我決定為整個通知服務創建一個單獨的劇集,以便每個人都更容易理解它

以上是在 NestJS 中使用 Passport 實作 JWT 驗證日(第 1 部分)的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn