java - 在登录方法上同步代码

标签 java multithreading authentication synchronized

我的网站中有以下 Java 代码片段

public boolean login(String username, string password){
    if(isUserLocked(username)){
        return false;
    }
    if(isPasswordCorrect(username, password)){
        return true;
    }
    else{
        increaseFailedAttempts(username);
        if(getFailedAttempts(username) > MAXIMUM_FAILED_ATTEMPTS)
        {
            lockUser(username);
        }

        return false;
    }
}

不幸的是,可以使用同时发送数百个请求的工具来破解此代码。在成功执行锁定用户的数据库调用之前,黑客可以强行猜测数百个用户/密码组合。我面临的是一个同步请求问题。天真的事情是在登录方法上同步。当然,这可以防止任何重复的请求被执行,但它也会将应用程序的速度降低到我们业务无法接受的程度。有哪些适契约(Contract)步的好做法?

以下方法与锁定用户相关,它们需要正确协同工作。

  • isUserLocked(): 去数据库检查字段“locked”是否已经被锁定 放。
  • increaseFailedAttempts():去数据库做 +1 的尝试 领域
  • getFailedAttempts():读取数据库以获取 尝试领域
  • lockUser():在数据库中为 用户。

最佳答案

为什么允许多次同时登录尝试?

// resizing is expensive, try to estimate the right size up front
private Map<String, Boolean> attempts = new ConcurrentHashMap<>(1024);

public void login(String username, String password) {
  // putIfAbsent returns previous value or null if there was no mapping for the key
  // not null => login for the username in progress
  // null => new user, proceed
  if (attempts.putIfAbsent(username, Boolean.TRUE) != null) {
    throw new RuntimeException("Login attempt in progress, request should be  discarded");
  }
  try {
    // this part remains unchanged
    // if the user locked, return false
    // if the password ok, reset failed attempts, return true
    // otherwise increase failed attempts
    //    if too many failed attempts, lock the user
    // return false
  } finally {
    attempts.remove(username);
  }
}

ConcurrentHashMap 不需要额外的同步,上面使用的操作是原子的。

当然,为了加快 isUserLocked 的速度,您可以将锁定状态缓存在 HashMap 或 HTTP 请求中——但必须谨慎实现。

仅在内存缓存中不是一个选项——如果合法用户将自己锁定在外,调用支持热线要求解锁,解锁状态已从数据库中删除,但由于内存缓存,用户仍然无法登录怎么办?

所以缓存的内容应该使用后台线程偶尔与数据库状态同步。

关于java - 在登录方法上同步代码,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38396330/

相关文章:

java - PostgreSql 中的事务

java - 带有 isEmpty() 更改通知的队列

c# - C#中线程安全的可破坏事件触发类

c - 多线程服务器如何工作?

c++ - 有效地从工作线程读取信息

python - Django - 身份验证,使用电子邮件确认注册

php - PostGreSQL 和 PHP 的登录脚本不起作用

java - 用于扫描的简单 try & catch 代码不适用于十进制值

java - 如何从 HashMap 获取值?

python - 尝试使用 stormpath 实现基于 oauth/token 的登录