#javascript #node.js #typescript #decorator #nestjs
#javascript #node.js #typescript #декоратор #nestjs
Вопрос:
Я хочу добиться чего-то подобного, используя nest.js : (что-то очень похожее на Spring framework)
@Controller('/test')
class TestController {
@Get()
get(@Principal() principal: Principal) {
}
}
После нескольких часов чтения документации я обнаружил, что nest.js поддерживает создание пользовательского декоратора. Итак, я решил реализовать свой собственный @Principal
декоратор. Декоратор отвечает за извлечение токена доступа из заголовка http и получение принципала пользователя из моей собственной службы аутентификации с использованием токена.
import { createParamDecorator } from '@nestjs/common';
export const Principal = createParamDecorator((data: string, req) => {
const bearerToken = req.header.Authorization;
// parse.. and call my authService..
// how to call my authService here?
return null;
});
Но проблема в том, что я понятия не имею, как получить мой экземпляр службы внутри обработчика декоратора. Возможно ли это? И как? Заранее благодарю вас
Ответ №1:
Невозможно внедрить службу в ваш пользовательский декоратор.
Вместо этого вы можете создать AuthGuard
, который имеет доступ к вашей службе. Затем защитник может добавить свойство к request
объекту, к которому вы затем можете получить доступ с помощью вашего пользовательского декоратора:
@Injectable()
export class AuthGuard implements CanActivate {
constructor(private authService: AuthService) {}
async canActivate(context: ExecutionContext): Promise<boolean> {
const request = context.switchToHttp().getRequest();
const bearerToken = request.header.Authorization;
const user = await this.authService.authenticate(bearerToken);
request.principal = user;
// If you want to allow the request even if auth fails, always return true
return !!user;
}
}
import { createParamDecorator } from '@nestjs/common';
export const Principal = createParamDecorator((data: string, req) => {
return req.principal;
});
а затем в вашем контроллере:
@Get()
@UseGuards(AuthGuard)
get(@Principal() principal: Principal) {
// ...
}
Обратите внимание, что nest предлагает некоторые стандартные модули для аутентификации, см. Документы.
Ответ №2:
для NestJS v7
Создайте пользовательский канал
// parse-token.pipe.ts
import { ArgumentMetadata, Injectable, PipeTransform } from '@nestjs/common';
import { AuthService } from './auth.service';
@Injectable()
export class ParseTokenPipe implements PipeTransform {
// inject any dependency
constructor(private authService: AuthService) {}
async transform(value: any, metadata: ArgumentMetadata) {
console.log('additional options', metadata.data);
return this.authService.parse(value);
}
}
Используйте этот канал с декоратором свойств
// decorators.ts
import { createParamDecorator, ExecutionContext } from '@nestjs/common';
import { ParseTokenPipe} from './parse-token.pipe';
export const GetToken = createParamDecorator((data: unknown, ctx: ExecutionContext) => {
return ctx.switchToHttp().getRequest().header.Authorization;
});
export const Principal = (additionalOptions?: any) => GetToken(additionalOptions, ParseTokenPipe);
Используйте этот декоратор с дополнительными опциями или без них
@Controller('/test')
class TestController {
@Get()
get(@Principal({hello: "world"}) principal) {}
}
Ответ №3:
Вы можете использовать промежуточную войну для всех контроллеров.
auth.middleware.ts
interface AccountData {
accId: string;
iat: number;
exp: number;
}
interface RequestWithAccountId extends Request {
accId: string;
}
@Injectable()
export class AuthMiddleware implements NestMiddleware {
constructor(private readonly authenticationService: AuthenticationService) {}
async use(req: RequestWithAccountId, res: Response, next: NextFunction) {
const token =
req.body.token || req.query.token || req.headers['authorization'];
if (!token) {
throw new UnauthorizedException();
}
try {
const {
accId,
}: AccountData = await this.authenticationService.verifyToken(token);
req.accId = accId;
next();
} catch (err) {
throw new UnauthorizedException();
}
}
}
Затем создайте декоратор AccountId
account-id.decorator.ts
import {
createParamDecorator,
ExecutionContext,
UnauthorizedException,
} from '@nestjs/common';
export const AccountId = createParamDecorator(
(data: unknown, ctx: ExecutionContext) => {
const req = ctx.switchToHttp().getRequest();
const token = req.accId;
if (!token) {
throw new UnauthorizedException();
}
return token;
},
);
Затем примените AccountId decorator в вашем контроллере
your.controller.ts
@Get()
async someEndpoint(
@AccountId() accountId,
) {
console.log('accountId',accontId)
}
Комментарии:
1. использование
async someEndPoint (@Request() req) { return req.accId }
is намного проще, нет необходимости использовать декоратор…