我正在尝试为graphql领域担任角色守卫。像这样:
import { Field, ObjectType } from 'type-graphql';
import { Column, Entity, JoinTable, ManyToMany, PrimaryGeneratedColumn } from 'typeorm';
import Role from '../role/role.entity';
@ObjectType()
@Entity()
class User {
@Field()
@PrimaryGeneratedColumn()
readonly id: number;
@Field()
@Column()
@Guard('USER_SEE_NAME') //this line
name: string;
@Field()
@Column()
surname: string;
}
export default User;
目的是如果用户没有所需的角色,则该字段将使用
null
值发送给客户端。我发现我应该使用class-transformer,但没有找到nestjs的任何示例。我也研究了nestjs documentation,但是只有内置装饰器的示例,它们没有在
ObjectType
中使用。我会使用Authorized装饰器,但是我需要访问nestjs上下文来获取userId,但我还没有找到一种方法。
您现在是否有一些示例或实现方法?
最佳答案
所以几天后,我找到了解决方案。我编写了一个自定义拦截器,如下所示:
import {
Injectable,
ExecutionContext,
CallHandler,
ClassSerializerInterceptor,
Inject,
} from '@nestjs/common';
// eslint-disable-next-line import/no-extraneous-dependencies
import { Observable } from 'rxjs';
// eslint-disable-next-line import/no-extraneous-dependencies
import { map } from 'rxjs/operators';
import { GqlExecutionContext } from '@nestjs/graphql';
import { ClassTransformOptions } from '@nestjs/common/interfaces/external/class-transform-options.interface';
import { PlainLiteralObject } from '@nestjs/common/serializer/class-serializer.interceptor';
import { CLASS_SERIALIZER_OPTIONS } from '@nestjs/common/serializer/class-serializer.constants';
import { loadPackage } from '@nestjs/common/utils/load-package.util';
import AuthService from './auth.service';
const REFLECTOR = 'Reflector';
let classTransformer: any = {};
@Injectable()
class ResourceInterceptor extends ClassSerializerInterceptor {
constructor(
@Inject(AuthService) private authService: AuthService,
@Inject(REFLECTOR) protected readonly reflector: any,
) {
super(reflector);
classTransformer = loadPackage('class-transformer', 'ClassSerializerInterceptor', () =>
// eslint-disable-next-line global-require
require('class-transformer'),
);
// eslint-disable-next-line global-require
require('class-transformer');
}
serializeCustom(
response: PlainLiteralObject | Array<PlainLiteralObject>,
options: ClassTransformOptions,
user: number,
): PlainLiteralObject | PlainLiteralObject[] {
const isArray = Array.isArray(response);
if (!(typeof response === 'object') && response !== null && !isArray) {
return response;
}
return isArray
? (response as PlainLiteralObject[]).map(item => this.transformToClass(item, options))
: this.transformToGuard(this.transformToClass(response, options), user);
}
transformToClass(plainOrClass: any, options: ClassTransformOptions): PlainLiteralObject {
return plainOrClass && plainOrClass.constructor !== Object
? classTransformer.classToClass(plainOrClass, options)
: plainOrClass;
}
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
const options = this.getContextOptionsCustom(context);
const ctx = GqlExecutionContext.create(context);
const { user } = ctx.getContext().req;
return next.handle().pipe(
map((res: PlainLiteralObject | Array<PlainLiteralObject>) => {
return this.serializeCustom(res, options, user);
}),
);
}
private getContextOptionsCustom(context: ExecutionContext): ClassTransformOptions | undefined {
return (
this.reflectSerializeMetadataCustom(context.getHandler()) ||
this.reflectSerializeMetadataCustom(context.getClass())
);
}
private reflectSerializeMetadataCustom(
obj: object | Function,
): ClassTransformOptions | undefined {
return this.reflector.get(CLASS_SERIALIZER_OPTIONS, obj);
}
async transformToGuard(response, userId: number) {
// eslint-disable-next-line no-restricted-syntax
for (const key of Object.keys(response)) {
const item = response[key];
// eslint-disable-next-line no-underscore-dangle
if (typeof item === 'object' && item !== null && item.__RESOURCE_GUARD__ === true) {
// eslint-disable-next-line no-await-in-loop
response[key] = (await this.authService.hasAccess(userId, item.resources))
? response[key].value
: null;
}
}
return response;
}
}
export default ResourceInterceptor;
用法:
@UseInterceptors(ResourceInterceptor)
async userGetLogged(@CurrentUser() userId: number) {
return this.userService.findById(userId);
}
关于javascript - NestJS graphql现场防护,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/60214536/