Brute force, lockout and auth events
Count failed logins with Spring Security events and lock an account before an attacker guesses it.
Open this lesson in the learning hubKey points
- Spring Security publishes an
AuthenticationSuccessEventand anAbstractAuthenticationFailureEventper attempt. - Listen with
@EventListenerand count failures per username and per source IP - either one alone is easy to dodge. - The lock lives on your user row:
isAccountNonLocked()returning false makes the provider throwLockedException. - Locked and disabled are checked before the password, so a locked account fails even once the attacker guesses right.
- Prefer a timed lock, say 15 minutes. A permanent one hands an attacker a denial-of-service switch for any known email.
- Never reveal which half was wrong. One "bad credentials" message for both stops attackers enumerating your users.
Example
@Component
public class LoginAttemptListener {
private final Map<String, Integer> failures = new ConcurrentHashMap<>();
private final UserRepository users;
@EventListener
public void onFailure(AbstractAuthenticationFailureEvent event) {
String name = String.valueOf(event.getAuthentication().getName());
int n = failures.merge(name, 1, Integer::sum);
if (n >= 5) {
users.lockUntil(name, Instant.now().plus(15, ChronoUnit.MINUTES));
}
}
@EventListener
public void onSuccess(AuthenticationSuccessEvent event) {
failures.remove(event.getAuthentication().getName());
}
}
Count failures from events, lock for a while, and leak nothing about why.
This is a reading copy. The full lesson — with the visual explainer, the interactive lab and a Run button for the code — lives in the Spring Security course, and every lesson in it is listed on the Spring Security contents page.