介紹

在本系列的前一章中,您學習了如何在 NestJS REST API 中處理關系數據。創建了一個模型,并在該模型與User模型之間建立了一對多的關系。同時,您還實現了UserArticle模型的CRUD端點。

在本章中,您將學習如何使用名為Passport的包向 API 添加身份驗證:

  1. 首先,您將使用名為Passport 的庫實現基于 JSON Web Token (JWT) 的身份驗證。
  2. 接下來,您將使用bcrypt庫對存儲在數據庫中的密碼進行哈希加密,以確保它們的安全性。

在本教程中,您將使用上一章中構建的 API 。

開發環境

要遵循本教程,您需要:

如果您沒有Unix shell環境(比如您使用的是Windows電腦),您仍然可以按照說明進行操作,但可能需要根據您的電腦系統對shell命令進行相應的調整。

克隆存儲庫

本教程的起點是本系列第二章的結尾。它包含一個使用 NestJS 構建的基本 REST API。

您可以在GitHub存儲庫的end-validation分支中找到本教程的起點。首先,請克隆該存儲庫并切換到相應的分支:

git clone -b end-relational-data git@github.com:prisma/blog-backend-rest-api-nestjs-prisma.git

現在,執行以下操作即可開始:

  1. 導航到克隆的目錄:

cd blog-backend-rest-api-nestjs-prisma

  1. 安裝依賴項:

npm install

  1. 使用 Docker 啟動 PostgreSQL 數據庫:

docker-compose up -d

  1. 應用數據庫遷移:

npx prisma migrate dev

  1. 啟動項目:

npm run start:dev

注意:步驟4還會生成Prisma客戶端,并為數據庫進行數據播種。

項目結構和文件

您克隆的存儲庫應具有以下結構:

median
├── node_modules
├── prisma
│ ├── migrations
│ ├── schema.prisma
│ └── seed.ts
├── src
│ ├── app.controller.spec.ts
│ ├── app.controller.ts
│ ├── app.module.ts
│ ├── app.service.ts
│ ├── main.ts
│ ├── articles
│ ├── users
│ └── prisma
├── test
│ ├── app.e2e-spec.ts
│ └── jest-e2e.json
├── README.md
├── .env
├── docker-compose.yml
├── nest-cli.json
├── package-lock.json
├── package.json
├── tsconfig.build.json
└── tsconfig.json

注意:您可能會注意到該文件夾??還帶有一個目錄。本教程不涉及測試。

此存儲庫中值得注意的文件和目錄是:

在 REST API 中實施身份驗證

在本部分中,您將為 REST API 實現大部分身份驗證邏輯。到本節結束時,以下端點將受到身份驗證保護??:

Web 上使用的身份驗證主要有兩種類型:基于會話的身份驗證和基于令牌的身份驗證。在本教程中,您將利用JSON Web令牌(JWT)來實現基于令牌的身份驗證機制。

首先,在您的應用程序中創建一個新模塊。運行以下命令生成新模塊:

npx nest generate resource

您將看到一些 CLI 提示。回答相應的問題:

  1. 您想為這個資源使用什么名稱(復數形式,例如,“users”)?認證
  2. 您使用什么傳輸層?REST API
  3. 您是否需要生成CRUD(創建、讀取、更新、刪除)的入口點?不

您現在應該auth在目錄中找到一個新模塊src/auth

安裝和配置

passport是 Node.js 應用程序的流行身份驗證庫。它具有高度可配置性并支持多種身份驗證策略。NestJS是基于Express Web框架構建的,因此它旨在與Express一起使用。NestJS 與passport進行了第一方集成@nestjs/passport,使其可以輕松地在您的 NestJS 應用程序中使用。

首先安裝以下軟件包:

npm install --save @nestjs/passport passport @nestjs/jwt passport-jwtnpm install --save-dev @types/passport-jwt

現在您已經安裝了所需的軟件包,您可以passport在應用程序中進行配置。打開src/auth.module.ts文件并添加以下代碼:

//src/auth/auth.module.ts
import { Module } from '@nestjs/common';
import { AuthService } from './auth.service';
import { AuthController } from './auth.controller';
import { PassportModule } from '@nestjs/passport';
import { JwtModule } from '@nestjs/jwt';
import { PrismaModule } from 'src/prisma/prisma.module';

export const jwtSecret = 'zjP9h6ZI5LoSKCRj';

@Module({
imports: [
PrismaModule,
PassportModule,
JwtModule.register({
secret: jwtSecret,
signOptions: { expiresIn: '5m' }, // e.g. 30s, 7d, 24h
}),
],
controllers: [AuthController],
providers: [AuthService],
})
export class AuthModule {}

@nestjs/passport模塊提供了一個PassportModule,您可以將其導入到您的應用程序中。這個PassportModulepassport庫的封裝器,為NestJS提供了特定的實用工具。您可以在官方文檔中閱讀更多關于PassportModule的詳細信息。

您還配置了JwtModule,用于生成和驗證JWT。JwtModulejsonwebtoken庫的封裝,它提供了一個secret密鑰用于簽署JWT,以及一個expiresIn對象來定義JWT的過期時間,當前設置為5分鐘。

注意:如果前一個令牌已過期,請記住生成新令牌。

您可以使用jwtSecret代碼片段中顯示的代碼片段或使用 OpenSSL 生成您自己的代碼片段。

注意:在真實的應用程序中,您不應該將機密直接存儲在代碼庫中。 NestJS提供了@nestjs/config從環境變量加載秘密的包。

實施端點

POST /login端點將用于對用戶進行身份驗證。它將接收用戶名和密碼作為輸入,如果憑據驗證通過,則返回JWT。首先,您需要創建一個LoginDto類,用于定義請求體的結構。

在目錄中創建一個名為login.dto.ts的新文件:

mkdir src/auth/dto
touch src/auth/dto/login.dto.ts

現在用emailLoginDto字段定義類:

//src/auth/dto/login.dto.ts
import { ApiProperty } from '@nestjs/swagger';
import { IsEmail, IsNotEmpty, IsString, MinLength } from 'class-validator';

export class LoginDto {
@IsEmail()
@IsNotEmpty()
@ApiProperty()
email: string;

@IsString()
@IsNotEmpty()
@MinLength(6)
@ApiProperty()
password: string;
}

您還需要定義一個新的AuthEntity來描述JWT有效負載的結構。請在auth.entity.ts文件中進行定義。

mkdir src/auth/entity
touch src/auth/entity/auth.entity.ts

現在在此文件中定義:

//src/auth/entity/auth.entity.ts
import { ApiProperty } from '@nestjs/swagger';

export class AuthEntity {
@ApiProperty()
accessToken: string;
}

AuthEntity只有一個名為accessToken的字符串字段,該字段包含JWT。

現在在里面創建一個新login方法:

//src/auth/auth.service.ts
import {
Injectable,
NotFoundException,
UnauthorizedException,
} from '@nestjs/common';
import { PrismaService } from './../prisma/prisma.service';
import { JwtService } from '@nestjs/jwt';
import { AuthEntity } from './entity/auth.entity';

@Injectable()
export class AuthService {
constructor(private prisma: PrismaService, private jwtService: JwtService) {}

async login(email: string, password: string): Promise<AuthEntity> {
// Step 1: Fetch a user with the given email
const user = await this.prisma.user.findUnique({ where: { email: email } });

// If no user is found, throw an error
if (!user) {
throw new NotFoundException(No user found for email: ${email}); } // Step 2: Check if the password is correct const isPasswordValid = user.password === password; // If password does not match, throw an error if (!isPasswordValid) { throw new UnauthorizedException('Invalid password'); } // Step 3: Generate a JWT containing the user's ID and return it return { accessToken: this.jwtService.sign({ userId: user.id }), }; } }

login方法首先獲取具有給定電子郵件的用戶。如果沒有找到用戶,系統會拋出一個NotFoundException。如果找到了用戶,系統會進一步檢查密碼是否正確。如果密碼不正確,則會拋出一個UnauthorizedException。如果密碼正確,它會生成一個包含用戶 ID 的 JWT 并將其返回。

現在,在AuthController中創建一個處理POST /auth/login請求的方法:

//src/auth/auth.controller.ts
+import { Body, Controller, Post } from '@nestjs/common';import { AuthService } from './auth.service';+import { ApiOkResponse, ApiTags } from '@nestjs/swagger';+import { AuthEntity } from './entity/auth.entity';+import { LoginDto } from './dto/login.dto';
@Controller('auth')+@ApiTags('auth')export class AuthController { constructor(private readonly authService: AuthService) {}
+ @Post('login')+ @ApiOkResponse({ type: AuthEntity })+ login(@Body() { email, password }: LoginDto) {+ return this.authService.login(email, password);+ }}

現在,您的API中應該有一個新的端點POST /auth/login

轉到該http://localhost:3000/api頁面并嘗試POST /auth/login端點。提供您在種子腳本中創建的用戶的憑據。

您可以使用以下請求正文:

{  "email": "sabin@adams.com",  "password": "password-sabin"}

執行請求后,您應該在響應中獲得 JWT。

POST /auth/login 端點

在下一部分中,您將使用此令牌對用戶進行身份驗證。

實施JWT認證策略

在 Passport 中,策略負責對請求進行身份驗證,這是通過實現身份驗證機制來完成的。在本部分中,您將實現用于對用戶進行身份驗證的 JWT 身份驗證策略。

您不會直接使用passport這個包,而是與@nestjs/passport這個包裝器包進行交互,后者會在幕后調用passport包。要使用配置策略@nestjs/passport,您需要創建一個擴展該類的類PassportStrategy。在這個課程中你需要做兩件主要的事情:

  1. 您將把 JWT 策略特定的選項和配置傳遞給super()構造函數中的方法。
  2. 一個validate()回調方法,它將與您的數據庫進行交互,根據JWT負載中的信息來獲取用戶。如果成功找到用戶,該validate()方法應當返回該用戶對象。

首先在jwt.strategy.ts目錄中創建一個名為src/auth/strategy的新文件:

touch src/auth/jwt.strategy.ts

現在實現JwtStrategy類:

//src/auth/jwt.strategy.ts
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { ExtractJwt, Strategy } from 'passport-jwt';
import { jwtSecret } from './auth.module';
import { UsersService } from 'src/users/users.service';

@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy, 'jwt') {
constructor(private usersService: UsersService) {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
secretOrKey: jwtSecret,
});
}

async validate(payload: { userId: number }) {
const user = await this.usersService.findOne(payload.userId);

if (!user) {
throw new UnauthorizedException();
}

return user;
}
}

您已經創建了一個名為JwtStrategy的類,該類擴展了PassportStrategy。該類PassportStrategy采用兩個參數:策略實現和策略名稱。在這里,您正在使用庫中的預定義策略passport-jwt

您在super()構造函數中向該方法傳遞了一些選項。其中,jwtFromRequest選項需要一個函數,該函數能夠從請求中提取JWT。在這種情況下,您將使用在API請求的Authorization頭部中提供Bearer令牌的標準方法。secretOrKey選項則告訴策略應該使用什么密鑰來驗證JWT。還有更多其他的選項可供配置,您可以在passport-jwt的官方存儲庫中查閱詳細信息。

對于passport-jwt,Passport 首先驗證 JWT 的簽名并解碼 JSON。然后將解碼后的 JSON 傳遞給該validate()方法。根據 JWT 簽名的工作方式,您可以保證收到之前由您的應用程序簽名和頒發的有效令牌。該validate()方法預計返回一個用戶對象。如果未找到用戶,該validate()方法將引發錯誤。

注意:Passport可能會讓人感到有些復雜。但將其本身視為一個迷你框架是很有幫助的,因為它將身份驗證過程抽象為了幾個步驟,這些步驟可以通過策略和配置選項進行自定義。

JwtStrategy中添加新的AuthModule作為提供程序:

//src/auth/auth.module.ts
import { Module } from '@nestjs/common';
import { AuthService } from './auth.service';
import { AuthController } from './auth.controller';
import { PassportModule } from '@nestjs/passport';
import { JwtModule } from '@nestjs/jwt';
import { PrismaModule } from 'src/prisma/prisma.module';
import { UsersModule } from 'src/users/users.module';
import { JwtStrategy } from './jwt.strategy';

export const jwtSecret = 'zjP9h6ZI5LoSKCRj';

@Module({
imports: [
PrismaModule,
PassportModule,
JwtModule.register({
secret: jwtSecret,
signOptions: { expiresIn: '5m' }, // e.g. 7d, 24h
}),
UsersModule,
],
controllers: [AuthController],
providers: [AuthService, JwtStrategy],
})
export class AuthModule {}

現在,JwtStrategy可以被其他模塊所使用了。此外,您還在UsersModule中添加了相應的imports,因為UsersService類中正在使用JwtStrategy

要使UsersServiceJwtStrategy類中可訪問,您還需要將其添加到exportsUsersModule中:

// src/users/users.module.ts
import { Module } from '@nestjs/common';
import { UsersService } from './users.service';
import { UsersController } from './users.controller';
import { PrismaModule } from 'src/prisma/prisma.module';

@Module({
controllers: [UsersController],
providers: [UsersService],
imports: [PrismaModule],
exports: [UsersService],
})
export class UsersModule {}

實施 JWT 身份驗證防護

Guards是一種 NestJS 構造,用于確定是否允許請求繼續進行。在本部分中,您將實現一個自定義JwtAuthGuard,用于保護需要身份驗證的路由。

jwt-auth.guard.ts在目錄中創建一個名為的新文件src/auth

touch src/auth/jwt-auth.guard.ts

現在實現JwtAuthGuard類:

//src/auth/jwt-auth.guard.tsimport { Injectable } from '@nestjs/common';import { AuthGuard } from '@nestjs/passport';
@Injectable()export class JwtAuthGuard extends AuthGuard('jwt') {}

該類AuthGuard需要策略的名稱。在本例中,您將使用JwtStrategy在上一節中實現的名為 的jwt

您現在可以使用這個守衛(Guard)作為裝飾器來保護您的端點。請將JwtAuthGuard添加到UsersController的路由中:

// src/users/users.controller.ts
import {
Controller,
Get,
Post,
Body,
Patch,
Param,
Delete,
ParseIntPipe,
UseGuards,
} from '@nestjs/common';
import { UsersService } from './users.service';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';
import { ApiCreatedResponse, ApiOkResponse, ApiTags } from '@nestjs/swagger';
import { UserEntity } from './entities/user.entity';
import { JwtAuthGuard } from 'src/auth/jwt-auth.guard';

@Controller('users')
@ApiTags('users')
export class UsersController {
constructor(private readonly usersService: UsersService) {}

@Post()
@ApiCreatedResponse({ type: UserEntity })
async create(@Body() createUserDto: CreateUserDto) {
return new UserEntity(await this.usersService.create(createUserDto));
}

@Get()
@UseGuards(JwtAuthGuard)
@ApiOkResponse({ type: UserEntity, isArray: true })
async findAll() {
const users = await this.usersService.findAll();
return users.map((user) => new UserEntity(user));
}

@Get(':id')
@UseGuards(JwtAuthGuard)
@ApiOkResponse({ type: UserEntity })
async findOne(@Param('id', ParseIntPipe) id: number) {
return new UserEntity(await this.usersService.findOne(id));
}

@Patch(':id')
@UseGuards(JwtAuthGuard)
@ApiCreatedResponse({ type: UserEntity })
async update(
@Param('id', ParseIntPipe) id: number,
@Body() updateUserDto: UpdateUserDto,
) {
return new UserEntity(await this.usersService.update(id, updateUserDto));
}

@Delete(':id')
@UseGuards(JwtAuthGuard)
@ApiOkResponse({ type: UserEntity })
async remove(@Param('id', ParseIntPipe) id: number) {
return new UserEntity(await this.usersService.remove(id));
}
}

如果您嘗試在未經身份驗證的情況下查詢任何這些端點,它將不再起作用。

`GET /users 端點給出 401 響應

在 Swagger 中集成身份驗證

目前,在Swagger上還沒有任何跡象表明這些端點受到了身份驗證的保護。您可以向控制器添加@ApiBearerAuth()裝飾器,以指示這些端點需要進行身份驗證:

// src/users/users.controller.ts

import {
Controller,
Get,
Post,
Body,
Patch,
Param,
Delete,
ParseIntPipe,
UseGuards,
} from '@nestjs/common';
import { UsersService } from './users.service';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';
import { ApiBearerAuth, ApiCreatedResponse, ApiOkResponse, ApiTags } from '@nestjs/swagger';
import { UserEntity } from './entities/user.entity';
import { JwtAuthGuard } from 'src/auth/jwt-auth.guard';

@Controller('users')
@ApiTags('users')
export class UsersController {
constructor(private readonly usersService: UsersService) {}

@Post()
@ApiCreatedResponse({ type: UserEntity })
async create(@Body() createUserDto: CreateUserDto) {
return new UserEntity(await this.usersService.create(createUserDto));
}

@Get()
@UseGuards(JwtAuthGuard)
@ApiBearerAuth()
@ApiOkResponse({ type: UserEntity, isArray: true })
async findAll() {
const users = await this.usersService.findAll();
return users.map((user) => new UserEntity(user));
}

@Get(':id')
@UseGuards(JwtAuthGuard)
@ApiBearerAuth()
@ApiOkResponse({ type: UserEntity })
async findOne(@Param('id', ParseIntPipe) id: number) {
return new UserEntity(await this.usersService.findOne(id));
}

@Patch(':id')
@UseGuards(JwtAuthGuard)
@ApiBearerAuth()
@ApiCreatedResponse({ type: UserEntity })
async update(
@Param('id', ParseIntPipe) id: number,
@Body() updateUserDto: UpdateUserDto,
) {
return new UserEntity(await this.usersService.update(id, updateUserDto));
}

@Delete(':id')
@UseGuards(JwtAuthGuard)
@ApiBearerAuth()
@ApiOkResponse({ type: UserEntity })
async remove(@Param('id', ParseIntPipe) id: number) {
return new UserEntity(await this.usersService.remove(id));
}
}

現在,受身份驗證保護的端點在 Swagger 中應該有一個鎖圖標 ??

Swagger 中的身份驗證受保護端點

目前無法直接在 Swagger 中“驗證”自己,因此您可以測試這些端點。為此,您可以在main.ts中的SwaggerModule設置里添加.addBearerAuth()方法調用:

// src/main.ts

import { NestFactory, Reflector } from '@nestjs/core';
import { AppModule } from './app.module';
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
import { ClassSerializerInterceptor, ValidationPipe } from '@nestjs/common';

async function bootstrap() {
const app = await NestFactory.create(AppModule);

app.useGlobalPipes(new ValidationPipe({ whitelist: true }));
app.useGlobalInterceptors(new ClassSerializerInterceptor(app.get(Reflector)));

const config = new DocumentBuilder()
.setTitle('Median')
.setDescription('The Median API description')
.setVersion('0.1')
.addBearerAuth()
.build();
const document = SwaggerModule.createDocument(app, config);
SwaggerModule.setup('api', app, document);

await app.listen(3000);
}
bootstrap();

現在,您可以通過單擊 Swagger 中的“授權”按鈕來添加令牌。 Swagger 會將令牌添加到您的請求中,以便您可以查詢受保護的端點。

注意:您可以通過向/auth/login端點發送一個包含有效emailpassword的POST請求來生成令牌。

自己嘗試一下。

Swagger 中的身份驗證工作流程

哈希密碼

目前,該User.password字段以純文本形式存儲。這是一個重大的安全風險,因為如果數據庫遭到泄露,那么所有密碼也將隨之暴露。為了解決這個問題,我們可以在將密碼存儲到數據庫之前先對其進行哈希處理。

您可以使用bcrypt加密庫來散列密碼。使用npm安裝它:

npm install bcrypt
npm install --save-dev @types/bcrypt

首先,您將更新中的createupdate方法以在將密碼存儲到數據庫之前對密碼進行哈希處理:

// src/users/users.service.ts
import { Injectable } from '@nestjs/common';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';
import { PrismaService } from 'src/prisma/prisma.service';
import * as bcrypt from 'bcrypt';

export const roundsOfHashing = 10;

@Injectable()
export class UsersService {
constructor(private prisma: PrismaService) {}

async create(createUserDto: CreateUserDto) {
const hashedPassword = await bcrypt.hash(
createUserDto.password,
roundsOfHashing,
);

createUserDto.password = hashedPassword;

return this.prisma.user.create({
data: createUserDto,
});
}

findAll() {
return this.prisma.user.findMany();
}

findOne(id: number) {
return this.prisma.user.findUnique({ where: { id } });
}

async update(id: number, updateUserDto: UpdateUserDto) {
if (updateUserDto.password) {
updateUserDto.password = await bcrypt.hash(
updateUserDto.password,
roundsOfHashing,
);
}

return this.prisma.user.update({
where: { id },
data: updateUserDto,
});
}

remove(id: number) {
return this.prisma.user.delete({ where: { id } });
}
}

bcrypt.hash函數接受兩個參數:哈希函數的輸入字符串和哈希輪數(也稱為成本因子)。增加哈希值的計算時間可以通過提高哈希輪數來實現,但這也需要在安全性和性能之間做出權衡。具體來說,哈希輪數越多,計算所需的時間就越長,從而有助于增強對暴力攻擊的防御能力。然而,更多輪的散列也意味著用戶登錄時需要更多的時間來計算散列。這個堆棧溢出的答案對這個主題進行了深入的討論。

bcrypt還會自動使用另一種稱為salting?的技術來增加暴力破解哈希的難度。salting是一種在散列之前將隨機字符串添加到輸入字符串的技術。這樣,由于每個密碼都使用了不同的鹽值,攻擊者就無法利用預先計算好的哈希表來破解密碼了。

您還需要更新數據庫種子腳本以在將密碼插入數據庫之前對密碼進行哈希處理:

// prisma/seed.ts
import { PrismaClient } from '@prisma/client';
import * as bcrypt from 'bcrypt';

// initialize the Prisma Client
const prisma = new PrismaClient();

const roundsOfHashing = 10;

async function main() {
// create two dummy users
const passwordSabin = await bcrypt.hash('password-sabin', roundsOfHashing);
const passwordAlex = await bcrypt.hash('password-alex', roundsOfHashing);

const user1 = await prisma.user.upsert({
where: { email: 'sabin@adams.com' },
update: {
password: passwordSabin,
},
create: {
email: 'sabin@adams.com',
name: 'Sabin Adams',
password: passwordSabin,
},
});

const user2 = await prisma.user.upsert({
where: { email: 'alex@ruheni.com' },
update: {
password: passwordAlex,
},
create: {
email: 'alex@ruheni.com',
name: 'Alex Ruheni',
password: passwordAlex,
},
});

// create three dummy posts
// ...
}

// execute the main function
// ...

運行種子腳本npx prisma db seed后,您應該會注意到數據庫中存儲的密碼都已經經過了哈希處理。

...
Running seed command ts-node prisma/seed.ts ... { user1: { id: 1, name: 'Sabin Adams', email: 'sabin@adams.com', password: '$2b$10$XKQvtyb2Y.jciqhecnO4QONdVVcaghDgLosDPeI0e90POYSPd1Dlu', createdAt: 2023-03-20T22:05:56.758Z, updatedAt: 2023-04-02T22:58:05.792Z }, user2: { id: 2, name: 'Alex Ruheni', email: 'alex@ruheni.com', password: '$2b$10$0tEfezrEd1a2g51lJBX6t.Tn.RLppKTv14mucUSCv40zs5qQyBaw6', createdAt: 2023-03-20T22:05:56.772Z, updatedAt: 2023-04-02T22:58:05.808Z }, ...

password字段的值對您來說會有所不同,因為每次都會使用不同的鹽值進行哈希處理。重要的是,現在這個值已經是一個哈希字符串了。

現在,如果您嘗試使用正確的密碼,您將面臨HTTP 401錯誤。這是因為該login方法嘗試將用戶請求中的明文密碼與數據庫中的哈希密碼進行比較。更新login方法以使用哈希密碼:

//src/auth/auth.service.ts
import { AuthEntity } from './entity/auth.entity';
import { PrismaService } from './../prisma/prisma.service';
import {
Injectable,
NotFoundException,
UnauthorizedException,
} from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import * as bcrypt from 'bcrypt';

@Injectable()
export class AuthService {
constructor(private prisma: PrismaService, private jwtService: JwtService) {}

async login(email: string, password: string): Promise<AuthEntity> {
const user = await this.prisma.user.findUnique({ where: { email } });

if (!user) {
throw new NotFoundException(No user found for email: ${email}); } const isPasswordValid = await bcrypt.compare(password, user.password); if (!isPasswordValid) { throw new UnauthorizedException('Invalid password'); } return { accessToken: this.jwtService.sign({ userId: user.id }), }; } }

您現在可以使用正確的密碼登錄并在響應中獲取 JWT。

總結和最后評論

在本章中,您學習了如何在 NestJS REST API 中實現 JWT 身份驗證。您還了解了對密碼進行加鹽處理以及將身份驗證與 Swagger 集成。

您可以在GitHub存儲庫的end-authentication分支上找到本教程的完成代碼。如果您發現問題,請隨時在存儲庫中提出問題或提交 PR。

原文鏈接:https://www.prisma.io/blog/nestjs-prisma-authentication-7D056s1s0k3l

上一篇:

Rest API 教程 – 完整的初學者指南

下一篇:

使用 TypeScript、PostgreSQL 和 Prisma 構建后端:持續集成與部署
#你可能也喜歡這些API文章!

我們有何不同?

API服務商零注冊

多API并行試用

數據驅動選型,提升決策效率

查看全部API→
??

熱門場景實測,選對API

#AI文本生成大模型API

對比大模型API的內容創意新穎性、情感共鳴力、商業轉化潛力

25個渠道
一鍵對比試用API 限時免費

#AI深度推理大模型API

對比大模型API的邏輯推理準確性、分析深度、可視化建議合理性

10個渠道
一鍵對比試用API 限時免費