Play Framework 如何故意延迟响应

问题描述 投票:0回答:3

我们有一个 Play 应用程序,目前使用的是 2.6 版。我们试图通过在用户提供失败密码时延迟向用户返回“登录失败”消息来防止对我们登录的字典攻击。我们目前哈希和加盐并拥有所有最佳实践,但我们不确定我们是否正确延迟。所以我们在控制器中有:

public Result login() { return ok(loginHtml) }

我们有一个:

public Result loginAction()
{ 
  // Check for user in database
  User user = User.find.query()...

  // Was the user found?
  if (user == null) {

     // Wrong password! Delay and redirect
     Thread.sleep(10000);  <<-- how do delay correctly?
     return redirect(routes.Controller.login())
  }

  // User is not null, so all good!
  ...

}

我们不确定

Thread.sleep(10000)
是否是延迟响应的最佳方式,因为这可能会挂起其他传入的请求,或者使用默认池中的太多线程。我们注意到,每秒点击次数低于 80 次以上时,Play Framework 不会将我们的 HTTP 调用路由到路由。也就是说,如果我们收到一个 HTTP POST 请求,我们的应用程序甚至会在 20 多秒后才将该请求发送给控制器,但是,在同一时间段内,如果我们收到一个 HTTP GET 请求,我们的应用程序将立即处理该 GET !

目前我们有 300 个线程作为默认分叉池的 Akka 设置中的最小值/最大值。任何见解将不胜感激。我们运行一个运行 Ubuntu 的 t2.xlarge AWS EC2 实例。

谢谢。

amazon-web-services playframework threadpool
3个回答
1
投票

Thread.sleep
导致当前线程阻塞,请尽量避免在生产代码中使用它。

您需要使用的是

CompletionStage
/
CompletableFuture
或任何用于处理异步编程和异步操作的抽象。

请查看有关异步操作的更多详细信息:https://www.playframework.com/documentation/2.8.x/JavaAsync

在您的案例中,解决方案看起来也很像(对不起,这可能有错误 - 我是 Scala 初级工程师):

import play.libs.concurrent.HttpExecutionContext;
import play.mvc.*;

import javax.inject.Inject;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;

public class LoginController extends Controller {

  private HttpExecutionContext httpExecutionContext;

  // Create and inject separate ScheduledExecutorService
  private ScheduledExecutorService executor; 

  @Inject
  public LoginController(HttpExecutionContext ec,
                         ScheduledExecutorService executor) {
    this.httpExecutionContext = ec;
    this.executor = executor;
  }

  public CompletionStage<Result> loginAction() {
    User user = User.find.query()...
    if (user == null) {
        return executor.schedule(() -> {redirect(routes.Controller.login());}, 10, TimeUnit.SECONDS);
    } else {
      // return another response
    }
  }
}

希望这有帮助!


0
投票

我一点也不喜欢这种做法。这会无缘无故地占用线程,如果有人发现您正在这样做并且他们有恶意的想法,可能会导致您的整个系统锁定。让我提出一个更好的方法:

User
表中存储上次登录尝试时间的可空
LocalDateTime

当您从数据库中获取用户时,检查上次尝试时间(与 LocalDateTime.now() 比较),如果自上次尝试以来已过去 10 秒,则执行密码比较。

如果密码不匹配,请将上次尝试时间存储为现在。

如果您提供良好的错误响应,这也可以在前端优雅地处理。

编辑:如果您想延迟不基于用户的登录尝试,您可以创建一个尝试表并按 IP 地址存储上次尝试。

如果你真的想按照我不建议的方式去做,你需要先阅读这篇文章:https://www.playframework.com/documentation/2.8.x/ThreadPools


0
投票

延迟响应确实惩罚了不小心输入错误密码的合法用户,我认为这不是一个好的应用程序设计。我在我的项目中做类似的事情,我在其中跟踪 ID 的失败计数,并在超过最大尝试次数时禁用 ID。在系统方面,我们将看起来可疑的 IP 列入黑名单。

© www.soinside.com 2019 - 2024. All rights reserved.