如果你對照了官方文檔,安裝JWT認(rèn)證的話步淹,你的controller可能類似于這樣
@Controller('auth/')
export class AuthController {
constructor(
private readonly authService: AuthService,
private readonly helper: Helper,
) {}
// 獲取token(使用local策略)
@UseGuards(LocalAuthGuard)
@Post('access-token')
async login(@Request() req) {
return this.authService.login(req.user);
}
// 解析token獲取user信息(使用Jwt策略)
@UseGuards(JwtAuthGuard)
@Get('user')
getProfile(@Request() req) {
return this.helper.getUser(); // 文檔上是 return this.request.user 我做了個(gè)封裝
}
}
看上去很美好脐湾,帶上用戶信息請求access-token就可以獲取到access_token犁珠,不帶上token請求user接口就會401,但是有一個(gè)問題丝蹭,要是這個(gè)項(xiàng)目有很多接口/方法慢宗,都要進(jìn)行jwt驗(yàn)證,難道要一個(gè)一個(gè)的@UseGuards(JwtStrategy)
去寫嗎奔穿,那多麻煩啊镜沽,還讓代碼變得丑陋,我們?yōu)槭裁床慌渲靡粋€(gè)默認(rèn)的Guard
巫橄,再需要不認(rèn)證的時(shí)候單獨(dú)指定呢淘邻?
先來看看nest.js官方提供的Guard可用作用域。
第一種:作用于方法
第二種:作用于controller
第三種:作用于全局
目前Nest只提供以上三種作用于湘换,如果你項(xiàng)目較大宾舅,接口較多,使用第一種和第二種豈不是要寫非常多的
@UseGuards(xxxxx)
彩倚,如果使用全局的話筹我,那我的獲取Token的access-token接口也會涼掉。我目前在進(jìn)行的項(xiàng)目有十五個(gè)模塊帆离,一個(gè)一個(gè)寫我可不愿意蔬蕊。于是我準(zhǔn)備自己實(shí)現(xiàn)一個(gè)上層Guard,來對不同的請求進(jìn)行不同的策略分配
第一步:創(chuàng)建一個(gè)自定義的上層Guard
auth.guard.ts
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { Observable } from 'rxjs';
import { Reflector } from '@nestjs/core';
@Injectable()
// 自定義Guard必須實(shí)現(xiàn)canActivate方法
export class AuthGuard implements CanActivate {
constructor(private readonly reflector: Reflector) {}
canActivate(context: ExecutionContext): boolean | Promise<boolean> | Observable<boolean> {
}
}
第二步:創(chuàng)建一個(gè)不需要認(rèn)證(NoAuth)的標(biāo)識位哥谷,讓部分接口不需要進(jìn)行jwt的驗(yàn)證
我在這里選擇創(chuàng)建一個(gè)裝飾器岸夯,使用SetMetadata()方法設(shè)置一個(gè)bool值麻献,用于告訴這個(gè)RoleAuthGuard,他不需要進(jìn)行jwt的策略認(rèn)證(我將它放在common下)
import { SetMetadata } from '@nestjs/common'
export const NoAuth = () => SetMetadata('no-auth', true);
第三步:返回完善RoleAuthGuard
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { Observable } from 'rxjs';
import { Reflector } from '@nestjs/core';
import { AuthGuard, IAuthGuard } from '@nestjs/passport';
@Injectable()
export class RoleAuthGuard implements CanActivate {
constructor(private readonly reflector: Reflector) {}
canActivate(context: ExecutionContext): boolean | Promise<boolean> | Observable<boolean> {
// 在這里取metadata中的no-auth猜扮,得到的會是一個(gè)bool
const noAuth = this.reflector.get<boolean>('no-auth', context.getHandler());
const guard = RoleAuthGuard.getAuthGuard(noAuth);
return guard.canActivate(context); // 執(zhí)行所選策略Guard的canActivate方法
}
// 根據(jù)NoAuth的t/f選擇合適的策略Guard
private static getAuthGuard(noAuth: boolean): IAuthGuard {
if (noAuth) {
return new (AuthGuard('local'))();
} else {
return new (AuthGuard('jwt'))();
}
}
}
第四步:設(shè)置這個(gè)Guard為全局使用
import { APP_FILTER, APP_GUARD } from '@nestjs/core';
import { AllExceptionsFilter } from '@/all-exception.filter';
import { RoleAuthGuard } from '@/auth/guards/role-auth.guard';
@Module({
imports: [...],
providers: [
// 此為過濾全局異常勉吻,可以忽略
{
provide: APP_FILTER,
useClass: AllExceptionsFilter,
},
// 設(shè)置全局守衛(wèi),useClass為自定義的Guard
{
provide: APP_GUARD,
useClass: RoleAuthGuard,
},
...
],
})
export class AppModule {}
最后旅赢,使用Postman進(jìn)行測試齿桃,默認(rèn)會使用Jwt策略,在不需要認(rèn)證的位置加上@NoAuth()即可