我想从返回的JSON中排除密码字段。 我正在使用NestJS和Typeorm。
this question上提供的解决方案对我或NestJS不起作用。如果需要,我可以发布我的代码。 还有其他想法或解决方案?感谢。
答案 0 :(得分:5)
我建议创建一个利用class-transformer库的拦截器:
@Injectable()
export class TransformInterceptor implements NestInterceptor {
intercept(
context: ExecutionContext,
call$: Observable<any>,
): Observable<any> {
return call$.pipe(map(data => classToPlain(data)));
}
}
然后,只需使用@Exclude()
装饰器排除属性,例如:
import { Exclude } from 'class-transformer';
export class User {
id: number;
email: string;
@Exclude()
password: string;
}
答案 1 :(得分:5)
此线程中有很多好的答案。基于上面的apun回答,我认为以下方法最不可能意外泄漏密码字段:
@Column({ select: false })
password: string
如果实体默认情况下未选择该字段,并且只能显式查询(例如,如果使用查询生成器,则通过addSelect()
进行查询),我认为某处存在滑移的可能性要小得多,并且对框架(最终是class-transformer
库)的“魔力”的依赖也越来越少,以确保安全。实际上,在许多项目中,唯一要明确选择的地方就是检查凭据的地方。
此方法还可以帮助防止密码哈希意外泄漏到日志条目等中,这是尚未提及的考虑因素。知道它不包含敏感信息,将其扔向用户对象要安全得多,尤其是如果它最终可能在某个地方的日志条目中序列化时。
总而言之,NestJS的记录方法是使用@Exclude()
装饰器,并且可接受的答案来自项目的创建者。
我肯定会经常使用Exclude()
装饰器,但不一定要使用密码或盐字段。
答案 2 :(得分:1)
作为Kamil's answer的补充:
您现在可以使用内置的ClassSerializerInterceptor
,而不是创建自己的拦截器,请参阅serialization docs。
@UseInterceptors(ClassSerializerInterceptor)
您可以在控制器类或其单个方法上使用它。用这种方法返回的每个实体都将使用class-transformer进行转换。
您可以通过在控制器或其方法上定义@SerializeOptions()
来自定义其行为:
@SerializeOptions({
excludePrefixes: ['_'],
groups: ['admin']
})
答案 3 :(得分:1)
您可以像这样覆盖模型的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);
}
}
通过使用plainToClass的class-transformer方法以及 @Exclude({toPlainOnly:true}),该密码将从JSON响应中排除,但在模型实例中可用。我喜欢此解决方案,因为它可将所有模型配置保留在实体中。
答案 4 :(得分:0)
您可以使用包https://github.com/typestack/class-transformer
您可以使用装饰器排除属性,也可以使用组排除属性。
答案 5 :(得分:0)
@Column({ select: false })
password: string
可以阅读有关隐藏列here
答案 6 :(得分:0)
答案 7 :(得分:0)
这已经是一个老话题了,但我还是想分享一下我的解决方案,也许它会对某人有所帮助。我使用 Express,但我的示例可能也适用于这种情况。
因此,在您的实体类中,您只需定义一个额外的静态 removePassword()
方法,该方法接收实体本身的实例,然后发送由该方法创建的对象,而不是您从 DB 获得的原始实体对象:>
import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';
@Entity({ name: 'users' })
export class User {
@PrimaryGeneratedColumn('uuid')
id: string | undefined;
@Column({ type: 'varchar', length: 100, unique: true })
email: string | undefined;
@Column({ type: 'text' })
password: string | undefined;
static removePassword(userObj: User) {
return Object.fromEntries(
Object.entries(userObj).filter(([key, val]) => key !== 'password')
);
}
}
这基本上就像在数组上调用 filter()
方法,但稍微复杂一点:您从条目数组中创建一个新对象(您最终将发送的对象),通过从原始条目数组(使用此精确的 filter()
方法)。
在你的路由处理程序中,你总是会做这样的事情:
import { Router, Request, Response, NextFunction } from 'express';
import { User } from '../../entity/User';
import { getRepository } from 'typeorm';
const router = Router();
router.post(
'/api/users/signin',
(req: Request, res: Response, next: NextFunction) => {
const { email } = req.body;
getRepository(User)
.findOne({ email })
.then(user =>
user ? res.send(User.removePassword(user)) : res.send('No such user:(')
)
.catch(err => next(new Error(err.message)));
}
);
export { router as signinRouter };
您也可以使用常规方法:
withoutPassword() {
return Object.fromEntries(
Object.entries(this).filter(([key, val]) => key !== 'password')
);
}
并在您的路由处理程序中:
res.send(user.withoutPassword());
答案 8 :(得分:0)
为了避免任何背痛和头痛,
我建议使用 plainToClass
以获得完整的猫鼬/类转换兼容性,并避免必须进行自定义覆盖来克服这个问题。
例如,将其添加到您的服务中:
async validateUser(email: string, password: string): Promise<UserWithoutPassword | null> {
const user = await this.usersService.findOne({ email });
if (user && await compare(password, user.password))
{
return plainToClass(UserWithoutPassword, user.toObject());
}
return null;
}
答案 9 :(得分:0)
对我有用的只是
使用@Exclude 注释字段并像这样覆盖 toJSON 模型方法
toJSON() { return classToPlain(this); }