Play Framework 如何故意延迟响应
Play Framework how to purposely delay a response
我们有一个 Play 应用程序,目前使用的是 2.6 版。我们正试图通过在用户提供密码失败时延迟 "failed login" 消息返回给我们的用户来防止对我们登录的字典攻击。我们目前哈希和加盐并拥有所有最佳实践,但我们不确定我们是否正确延迟。所以我们在控制器中有:
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 请求,在同一时间段内,我们的应用程序将立即获取的过程!
目前我们的 Akka 设置中有 300 个线程作为默认分叉池的 min/max。任何见解将不胜感激。我们 运行 一个 t2.xlarge AWS EC2 实例 运行ning Ubuntu.
谢谢。
我完全不喜欢这种方法。这会无缘无故地占用线程,如果有人发现您正在这样做并且他们有恶意的想法,可能会导致您的整个系统锁定。让我提出一个更好的方法:
在 User
table 中存储上次登录尝试时间的可为空 LocalDateTime
。
当您从数据库中获取用户时,检查上次尝试时间(与 LocalDateTime.now() 比较),如果自上次尝试以来已过去 10 秒,则执行密码比较。
如果密码不匹配,则将上次尝试时间存储为现在。
如果您提供良好的错误响应,这也可以在前端优雅地处理。
编辑:如果您想延迟不基于用户的登录尝试,您可以创建一个尝试 table 并按 IP 地址存储上次尝试。
如果你真的想按照我不推荐的方式去做,你需要先阅读这篇文章:https://www.playframework.com/documentation/2.8.x/ThreadPools
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
}
}
}
希望对您有所帮助!
我们有一个 Play 应用程序,目前使用的是 2.6 版。我们正试图通过在用户提供密码失败时延迟 "failed login" 消息返回给我们的用户来防止对我们登录的字典攻击。我们目前哈希和加盐并拥有所有最佳实践,但我们不确定我们是否正确延迟。所以我们在控制器中有:
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 请求,在同一时间段内,我们的应用程序将立即获取的过程!
目前我们的 Akka 设置中有 300 个线程作为默认分叉池的 min/max。任何见解将不胜感激。我们 运行 一个 t2.xlarge AWS EC2 实例 运行ning Ubuntu.
谢谢。
我完全不喜欢这种方法。这会无缘无故地占用线程,如果有人发现您正在这样做并且他们有恶意的想法,可能会导致您的整个系统锁定。让我提出一个更好的方法:
在 User
table 中存储上次登录尝试时间的可为空 LocalDateTime
。
当您从数据库中获取用户时,检查上次尝试时间(与 LocalDateTime.now() 比较),如果自上次尝试以来已过去 10 秒,则执行密码比较。
如果密码不匹配,则将上次尝试时间存储为现在。
如果您提供良好的错误响应,这也可以在前端优雅地处理。
编辑:如果您想延迟不基于用户的登录尝试,您可以创建一个尝试 table 并按 IP 地址存储上次尝试。
如果你真的想按照我不推荐的方式去做,你需要先阅读这篇文章:https://www.playframework.com/documentation/2.8.x/ThreadPools
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
}
}
}
希望对您有所帮助!