【发布时间】: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