本系列文已出版成書「NestJS 基礎必學實務指南:使用強大且易擴展的 Node.js 框架打造網頁應用程式」,感謝 iT 邦幫忙與博碩文化的協助。如果對 NestJS 有興趣、覺得這個系列文對你有幫助的話,歡迎前往購書,你的支持是我最大的寫作動力!
Guard 是一種檢測機制,就像公司的保全系統,需要使用門禁卡才能進入,否則就會被擋在門外。這樣的機制經常用在身份驗證與授權,當有未經授權的請求時,將會由 Guard 攔截並擋下。Express 的 Guard 經常在 Middleware 層做處理,這樣的處理方式並沒有不好,只是要能夠清楚掌握呼叫 next()
之後會執行什麼,相較之下,Nest 多設計了 Guard 更能確保它的執行順序,從下圖可以看出 Guard 是執行在 Middleware 之後、Interceptor 之前:
Guard 可以透過 CLI 產生:
$ nest generate guard <GUARD_NAME>
注意:
<GUARD_NAME>
可以含有路徑,如:guards/auth
,這樣就會在src
資料夾下建立該路徑並含有 Guard。
這邊我建立一個 AuthGuard
在 guards
資料夾下:
$ nest generate guard guards/auth
在 src
底下會看見一個名為 guards
的資料夾,裡面有 auth.guard.ts
以及 auth.guard.spec.ts
:
建立出來的 Guard 骨架如下,會發現 Guard 其實也是帶有 @Injectable
裝飾器的 class
,不過它必須實作 CanActivate
介面,並設計 canActivate(context: ExecutionContext)
方法:
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
import { Observable } from 'rxjs';
@Injectable()
export class AuthGuard implements CanActivate {
canActivate(
context: ExecutionContext,
): boolean | Promise<boolean> | Observable<boolean> {
return true;
}
}
canActivate(context: ExecutionContext)
可以是同步或非同步的,所以回傳值可以是 boolean
、Promise<boolean>
或 Observable<boolean>
,如果要讓驗證能夠通過,就必須讓最終結果為 true
。
提醒:會提供
ExecutionContext
是因為可以從這裡取出需要用來驗證的資料。
在使用之前,先將 auth.guard.ts
修改一下,刻意將結果回傳 false
,並採用非同步做法:
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
import { Observable, of } from 'rxjs';
import { delay } from 'rxjs/operators';
@Injectable()
export class AuthGuard implements CanActivate {
canActivate(
context: ExecutionContext,
): boolean | Promise<boolean> | Observable<boolean> {
return of(false).pipe(delay(2000));
}
}
接著建立 TodoModule
與 TodoController
:
$ nest generate module features/todo
$ nest generate controller features/todo
提醒:如果已經建立過可以略過此步驟。
透過 @UseGuards
裝飾器即可輕鬆套用,使用的方式大致上可以分成兩種:
@UseGuards
裝飾器,只會針對該資源套用。@UseGuards
裝飾器,會針對整個 Controller 中的資源套用。下方以套用在 Controller 為例,修改 todo.controller.ts
:
import { Controller, Get, UseGuards } from '@nestjs/common';
import { AuthGuard } from '../../guards/auth.guard';
@Controller('todos')
@UseGuards(AuthGuard)
export class TodoController {
@Get()
getAll() {
return [];
}
}
透過瀏覽器查看 http://localhost:3000 會發現被 Guard 擋掉了:
{
"statusCode": 403,
"message":"Forbidden resource",
"error":"Forbidden"
}
如果整個 App 都需要使用 Guard,那就可以直接將其掛在全域下,只需要修改 main.ts
即可,透過 useGlobalGuards
來配置全域 Guard:
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { AuthGuard } from './guards/auth.guard';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalGuards(new AuthGuard());
await app.listen(3000);
}
bootstrap();
上面的方法是透過模組外部完成全域配置的,透過指定 Provider 的 token 為 APP_GUARD
來實現,這裡是用 useClass
來指定要建立實例的類別:
import { Module } from '@nestjs/common';
import { APP_GUARD } from '@nestjs/core';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { TodoModule } from './features/todo/todo.module';
import { AuthGuard } from './guards/auth.guard';
@Module({
imports: [TodoModule],
controllers: [AppController],
providers: [
AppService,
{
provide: APP_GUARD,
useClass: AuthGuard
}
]
})
export class AppModule {}
Guard 是實作授權與身份驗證的好幫手,也是非常常用的功能,後面篇幅會出現 Guard 的相關應用。下方是今天的懶人包:
ExecutionContext
取得驗證相關資訊。