我的网站中有以下 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/