如何从控制器JSON返回的值中排除实体字段。NestJS + Typeorm

Vla*_*kov 4 node.js typescript typeorm nestjs

我想从返回的JSON中排除密码字段。我正在使用NestJS和Typeorm。

针对此问题提供的解决方案不适用于我或NestJS。如果需要,我可以发布我的代码。还有其他想法或解决方案吗?谢谢。

Fra*_*ále 26

您可以像这样覆盖模型的 toJSON 方法。

@Entity()
export class User extends BaseAbstractEntity implements IUser {
  static passwordMinLength: number = 7;

  @ApiModelProperty({ example: faker.internet.email() })
  @IsEmail()
  @Column({ unique: true })
  email: string;

  @IsOptional()
  @IsString()
  @MinLength(User.passwordMinLength)
  @Exclude({ toPlainOnly: true })
  @Column({ select: false })
  password: string;

  @IsOptional()
  @IsString()
  @Exclude({ toPlainOnly: true })
  @Column({ select: false })
  passwordSalt: string;

  toJSON() {
    return classToPlain(this);
  }

  validatePassword(password: string) {
    if (!this.password || !this.passwordSalt) {
      return false;
    }
    return comparedToHashed(password, this.password, this.passwordSalt);
  }
}
Run Code Online (Sandbox Code Playgroud)

通过使用 plainToClass 的 class-transformer 方法和@Exclude({ toPlainOnly: true }),密码将从 JSON 响应中排除,但在模型实例中可用。我喜欢这个解决方案,因为它保留了实体中的所有模型配置。

  • toJSON 方法是可以在任何 javascript 类中重写的方法。它定义了如何将类转换为常规对象。当调用 JSON.stringify 时,会调用该类的 toJSON 方法。https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify 请参阅说明部分。 (2认同)

Kim*_*ern 22

作为卡米尔回答的补充:

您现在可以使用内置的ClassSerializerInterceptor,而不是创建自己的拦截器,请参阅序列化文档

@UseInterceptors(ClassSerializerInterceptor)
Run Code Online (Sandbox Code Playgroud)

您可以在控制器类或其各个方法上使用它。这种方法返回的每个实体都将使用 class-transformer 进行转换,因此将@Exclude注释考虑在内:

import { Exclude } from 'class-transformer';

export class User {
    /** other properties */    

    @Exclude()
    password: string;
}
Run Code Online (Sandbox Code Playgroud)

您可以通过@SerializeOptions()在控制器或其方法上定义来自定义其行为:

@SerializeOptions({
  excludePrefixes: ['_'],
  groups: ['admin']
})
Run Code Online (Sandbox Code Playgroud)

例如,仅向某些用户公开某些字段:

@Expose({ groups: ["admin"] })
adminInfo: string;
Run Code Online (Sandbox Code Playgroud)


fir*_*orx 9

这个线程中有很多很好的答案。以上面 apun 的回答为基础,我认为以下方法最不可能意外泄露密码字段:

@Column({ select: false })
password: string
Run Code Online (Sandbox Code Playgroud)

如果实体默认不选择该字段,并且只能显式查询(例如,通过addSelect()使用查询构建器),我认为某处存在错误的可能性要小得多,并且对“框架(最终是class-transformer库)的魔法”以确保安全性。实际上,在许多项目中,您唯一明确选择它的地方就是您检查凭据的地方。

这种方法还可以帮助防止密码哈希意外泄漏到日志条目等中,这是尚未提及的考虑因素。在知道它不包含敏感信息的用户对象周围折腾感觉更安全,特别是如果它最终可能会在某个日志条目中被序列化。

总而言之,NestJS 的文档化方法是使用@Exclude()装饰器,并且接受的答案来自项目的创始人。

我肯定经常使用Exclude()装饰器,但不一定用于密码或盐字段。


Kam*_*iec 5

我建议创建一个利用class-transformer库的拦截器:

@Injectable()
export class TransformInterceptor implements NestInterceptor {
  intercept(
    context: ExecutionContext,
    call$: Observable<any>,
  ): Observable<any> {
    return call$.pipe(map(data => classToPlain(data)));
  }
}
Run Code Online (Sandbox Code Playgroud)

然后,只需使用@Exclude()装饰器排除属性,例如:

import { Exclude } from 'class-transformer';

export class User {
    id: number;
    email: string;

    @Exclude()
    password: string;
}
Run Code Online (Sandbox Code Playgroud)

  • 如果我没记错的话,可以在此处使用内置的“ClassSerializerInterceptor” (2认同)
  • 有没有办法用棱镜来做到这一点?Prisma 似乎根本不使用实体流:( (2认同)

apu*_*pun 5

@Column({ select: false })
password: string
Run Code Online (Sandbox Code Playgroud)

可以在此处阅读有关隐藏列的信息

  • 请不要仅发布代码作为答案,还要提供解释您的代码的作用以及它如何解决问题的问题。带解释的答案通常更有帮助,质量也更好,并且更有可能吸引点赞。 (2认同)