[英]NestJS: Adding verification options to AuthGuard with JWT
我正在嘗試按照文檔使用AuthGuard
裝飾器和通行證 JWT 策略。
文檔中的所有內容都很好用。 但是我現在想保護具有包含在 JWT 中的范圍的路由。 所以這是我的應用程序生成的基本 jwt 負載:
{
"user": {
"id": "20189c4f-1183-4216-8b48-333ddb825de8",
"username": "user.test@gmail.com"
},
"scope": [
"manage_server"
],
"iat": 1534766258,
"exp": 1534771258,
"iss": "15f2463d-8810-44f9-a908-801872ded159",
"sub": "20189c4f-1183-4216-8b48-333ddb825de8",
"jti": "078047bc-fc1f-4c35-8abe-72834f7bcc44"
}
這是由AuthGuard
裝飾器保護的基本受保護路由:
@Get('protected')
@UseGuards(AuthGuard('jwt'))
async protected(): Promise<string> {
return 'Hello Protected World';
}
我想添加選項並將該路由的訪問權限限制為將manager_server
范圍納入其 JWT 的人員。 因此,在閱讀了一些AuthGuard
代碼后,我認為我可以編寫如下內容:
@Get('protected')
@UseGuards(AuthGuard('jwt', {
scope: 'manage_server'
}))
async protected(): Promise<string> {
return 'Hello Protected World';
}
但是,我在文檔中看不到可以使用此選項的地方。
我認為向JWTStrategy
的validate
函數添加一個選項參數可以解決問題,但事實並非如此。 這是我的validate
函數(包含在jwt.strategy.ts
文件中):
async validate(payload: JwtPayload, done: ((err: any, value: any) => void)) {
const user = await this.authService.validateUser(payload);
if (!user) {
return done(new UnauthorizedException(), false);
}
done(null, user);
}
非常感謝您的幫助,如果您需要,請隨時在評論中向我詢問更多信息。
當您查看 AuthGuard 的代碼時,似乎options.callback
函數是唯一可能的自定義。
我認為AuthGuard
編寫自己的支持范圍檢查的ScopesGuard
, RolesGuard
使用ScopesGuard
(或RolesGuard
)和自己的裝飾@Scopes('manage_server')
如@Scopes('manage_server')
代替)。 為此,您只需按照docs 中的RolesGuard
示例進行操作,該示例也僅檢查請求中user
屬性下的 JWT 負載的屬性。
創建一個@Scopes()
裝飾器:
export const Scopes = (...scopes: string[]) => SetMetadata('scopes', scopes);
創建一個ScopesGuard
:
@Injectable()
export class ScopesGuard implements CanActivate {
constructor(private readonly reflector: Reflector) {}
canActivate(context: ExecutionContext): boolean {
const scopes = this.reflector.get<string[]>('scopes', context.getHandler());
if (!scopes) {
return true;
}
const request = context.switchToHttp().getRequest();
const user = request.user;
const hasScope = () => user.scopes.some((scope) => scopes.includes(scope));
return user && user.scopes && hasScope();
}
}
使用 ScopesGuard 作為所有路由的全局保護(當沒有給出范圍時返回 true):
@Module({
providers: [
{
provide: APP_GUARD,
useClass: ScopesGuard,
},
],
})
export class ApplicationModule {}
然后在端點上使用它:
@Get('protected')
@UseGuards(AuthGuard('jwt'))
@Scopes('manage_server')
async protected(): Promise<string> {
return 'Hello Protected World';
}
我嘗試了一種稍微不同的方法,通過擴展 AuthGuard 保護。 我想保持使用不同 Passport 策略的能力,所以我包含了一個 mixin。 反饋表示贊賞。
在您的 Jwt 策略中,您可以簡單地返回 JwtPaylozd,以便用戶具有 scopes 屬性。 然后自定義 AuthGuard 如下所示:
import { UnauthorizedException, mixin } from "@nestjs/common";
import { AuthGuard } from "@nestjs/passport";
export function AuthScopes(scopes: string[], type?: string | string[]) {
return mixin(class ScopesAuth extends AuthGuard(type) {
protected readonly scopes = scopes;
handleRequest(err, user, info, context) {
if (err || !user) {
throw err || new UnauthorizedException();
}
if(!this.scopes.some(s => user.scopes.split(' ').includes(s)))
{
throw new UnauthorizedException(`JWT does not possess one of the required scopes (${this.scopes.join(',')})`);
}
return user;
}
});
}
然后你可以像這樣使用這個守衛:
@Get('protected')
@UseGuards(AuthScopes(['secret:read'], 'jwt'))
async protected(): Promise<string> {
return 'Hello Protected World';
}
'jwt' 代表策略。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.