【问题标题】:NestJS & Passport - Setting response headers in guardNestJS 和 Passport - 在警卫中设置响应标头
【发布时间】:2020-11-08 02:55:28
【问题描述】:

在我的 NestJS 应用程序中,我使用 本地护照策略 来保护登录路由,然后返回一个 jwt。此过程正常工作。

现在我在本地策略中实现了逻辑以防止暴力破解(除了 main.ts 中的总体速率限制),如 here 所述。当我抛出 TooManyRequests HttpException 时,我还想设置“Retry-After”标头,以便能够在前端为用户提供有用的信息。但是我无权访问警卫中的响应对象。我试图实现一个没有帮助的拦截器。此外,我只能访问本地策略中“Retry-after”的计算值,因为该值是在那里计算的。

设置此标头的正确方法是什么?这是我的代码还不能正常工作。

@Injectable()
export class LocalStrategy extends PassportStrategy(Strategy) {

private maxWrongAttemptsByIpPerDay = 100;
private maxConsecutiveFailsByUsernameAndIp = 5;

private  limiterSlowBruteByIp = new RateLimiterMongo({
    storeClient: this.connection,
    keyPrefix: 'login_fail_ip_per_day',
    points: this.maxWrongAttemptsByIpPerDay,
    duration: 60 * 60 * 24,
    blockDuration: 60 * 60 * 24, // Block for 1 day, if 100 wrong attempts per day
})

public limiterConsecutiveFailsByUsernameAndIp = new RateLimiterMongo({
    storeClient: this.connection,
    keyPrefix: 'login_fail_consecutive_username_and_ip',
    points: this.maxConsecutiveFailsByUsernameAndIp,
    duration: 60 * 60 * 24 * 90, // Store number for 90 days since first fail
    blockDuration: 60 * 60 // Block for 1 hour
})

constructor(private authService: AuthService, @InjectConnection() private connection: Connection) {
    super({passReqToCallback: true});
}

async validate(req: Request, username: string, password: string): Promise<any> {

    const usernameIpKey = this.authService.getUsernameIPkey(username, req['ip'])

    const [resUsernameAndIp, resSlowByIp] = await Promise.all([
        this.limiterConsecutiveFailsByUsernameAndIp.get(usernameIpKey),
        this.limiterSlowBruteByIp.get(req['ip'])
    ]);

    let retrySecs = 0;

    // Check if IP or Username + IP is already blocked
    if (resSlowByIp !== null && resSlowByIp.consumedPoints > this.maxWrongAttemptsByIpPerDay) {
        retrySecs = Math.round(resSlowByIp.msBeforeNext / 1000) || 1;
    } else if (resUsernameAndIp !== null && resUsernameAndIp.consumedPoints > this.maxConsecutiveFailsByUsernameAndIp) {
        retrySecs = Math.round(resUsernameAndIp.msBeforeNext / 1000) || 1;
    }

    if (retrySecs > 0) {
        // res.set('Retry-After', String(retrySecs));
        throw new HttpException('Too many requests', HttpStatus.TOO_MANY_REQUESTS);
    } else {
        const user = await this.authService.validateUser(username, password);
        if (!user) {
            // Consume 1 point from limiters on wrong attempt and block if limits reached
            try {
                const promises = [this.limiterSlowBruteByIp.consume(req['ip'])];
                if (!user) {
                    // Count failed attempts by Username + IP only for registered users
                    promises.push(this.limiterConsecutiveFailsByUsernameAndIp.consume(usernameIpKey));
                }

                await promises;

                throw new UnauthorizedException();

            } catch (rlRejected) {
                if (rlRejected instanceof Error) {
                    throw rlRejected;
                } else {
                    // res.set('Retry-After', String(Math.round(rlRejected.msBeforeNext / 1000) || 1));
                    throw new HttpException('Too many requests', HttpStatus.TOO_MANY_REQUESTS);
                }
            }
        } else {
            if (resUsernameAndIp !== null && resUsernameAndIp.consumedPoints > 0) {
                // Reset on successful authorisation
                await this.limiterConsecutiveFailsByUsernameAndIp.delete(usernameIpKey);
            }

            return user;
        }
    }
}

}

非常感谢您!

【问题讨论】:

    标签: authentication passport.js nestjs passport-local


    【解决方案1】:

    您有几个选择,而不是像您当前那样使用该策略。

    1. 就像你说的,在 guard 中抛出你可以访问 ExecutionContext 的异常,这样你就可以 context.switchToHttp().getResponse() 获取响应对象并能够根据需要设置标题(你'目前正在尝试策略文件中执行此操作)

    2. 使用像 nestjs-throttler 这样的包并使用它的装饰器来帮助设置速率限制

    【讨论】:

      猜你喜欢
      • 2020-08-25
      • 2023-03-16
      • 2021-09-02
      • 2020-08-30
      • 2021-09-24
      • 2016-10-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多