Spring如何以非阻塞方式匹配bcrypt密码

Posted

技术标签:

【中文标题】Spring如何以非阻塞方式匹配bcrypt密码【英文标题】:Spring how to match bcrypt password in a non-blocking way 【发布时间】:2020-11-23 22:58:07 【问题描述】:

嘿,我正在使用 webflux 创建一个 rest api 用于学习目的,但我在登录页面遇到 bcrypt 密码匹配问题。

我的登录请求花费了额外的时间,当我挖掘更多时,我发现匹配功能是一项繁重的任务和耗时,我担心它会影响整个应用程序的性能

下面是我的代码

.filter(user -> 
    long s = System.currentTimeMillis();
    boolean matches = bCryptPasswordEncoder.matches(loginRequest.getPassword(), user.getPassword());
    long e = System.currentTimeMillis();
    System.out.println(e-s);
    return matches;
)

我的整个 api 响应时间是 70 毫秒,时间差异是打印 62、63,这意味着匹配正在减慢我的整个应用程序。如果我一次收到 50 个登录请求怎么办?我想知道以非阻塞方式执行此操作的正确方法是什么。

【问题讨论】:

如果您使用的是spring security,那您为什么要自己匹配密码,而不是使用内置登录功能的框架?此外,70ms 相当快。您不想要快速密码匹配,因为恶意行为者可以使用它来快速尝试密码。 BCrypt 是一种所谓的“慢”算法,应该调整为在运行的系统上每秒匹配一次密码,以防止密码喷射攻击。 你可以在这里阅读更多关于密码的信息docs.spring.io/spring-security/site/docs/current/reference/… 但我的问题是匹配是否需要时间,并且由于我们处于有限线程上,它可能会暂停线程 60 毫秒,其他请求可能会受到影响。我错了吗? 可能取决于实现,如果是这种情况,那么它应该放在自己的调度程序上。除非您进行负载测试,否则无法确定。这就是为什么我仍然建议您使用内置的登录实现,不要尝试构建自己的。 【参考方案1】:

当 CPU 基本上处于空闲状态时,操作会“阻塞”,等待其他事情完成,然后才能进行更多工作。这可能是网络请求、磁盘访问、时间延迟等。

你这里的不是这个,所以它不是阻塞操作。相反,您有一个 CPU 密集型 操作,这就是 BCrypt 的重点(简而言之,故意检查密码是否有效很难防止暴力攻击。)

没有办法“免费”加速这个过程(否则你已经破坏了 BCrypt),但你可以根据具体情况采取一些方法。从“最少工作”到“最多工作”:

70 毫秒,用于测试/学习目的是可以的。不要拘泥于细节,在这种情况下接受它。 通过调用parallel()subscribeOn(Schedulers.parallel()) 使通量并行化,使您的反应链能够使用所有CPU 内核。这可能是最适合“学习与发展”的场景。 如果出现问题,请使用更多硬件解决问题,例如容器化和使用 Kubernetes 或类似的基于需求的自动扩展。 将 BCrypt 生成一个单独的微服务,根据需要自动扩展,然后通过调用该微服务(并使用 flatMap())以非阻塞方式处理密码检查。

【讨论】:

如果我使用并行,那么仍然有限制,因为反应器创建的线程等于 4 核处理器的核心。那么如果 100 个人同时请求呢? @MuhammadIlyas 然后,假设您将其并行化到 4 个核心,然后信封计算表明所有这些登录请求都需要几秒钟的时间才能执行。不是世界末日——但如果你真的看到了如此大的流量,你可能还是想升级你的托管硬件。正如答案中所说,没有简单的方法解决它。假设您不想放弃 BCrypt 而不是使用 CPU 密集度较低的设置,那么您唯一可行的选择就是以某种方式在问题上投入更多硬件。

以上是关于Spring如何以非阻塞方式匹配bcrypt密码的主要内容,如果未能解决你的问题,请参考以下文章

如何以非阻塞方式压缩文件

如何以非阻塞、不可中断的方式从类中运行 C++ 函数 [关闭]

如何以非阻塞方式链接期货?也就是说,如何在不阻塞的情况下将一个future用作另一个future的输入?

如何以非交互方式为“psql”指定密码?

以非阻塞方式打开 QDialog

Spring Boot 密码 bcrypt 编码器:编码值与在线生成的不匹配