Is there some configuration or available module in Spring Security to limit login attempts (ideally, I'd like to have an increasing wait time between subsequent failed attempts)? If not, which part of the API should be used for this?
问题:
回答1:
Implement an AuthenticationFailureHandler that updates a count/time in the DB. I wouldn't count on using the session because the attacker is not going to be sending cookies anyway.
回答2:
From Spring 4.2 upwards annotation based event listeners are available:
@Component
public class AuthenticationEventListener {
@EventListener
public void authenticationFailed(AuthenticationFailureBadCredentialsEvent event) {
String username = (String) event.getAuthentication().getPrincipal();
// update the failed login count for the user
// ...
}
}
回答3:
I recently implemented a similar functionality to monitor login failures using JMX. Please see the code in my answer to question Publish JMX notifications in using Spring without NotificationPublisherAware. An aspect on the authenticate method of authentication provider updates MBean and works with a notification listener (code not shown in that question) to block user and IP, send alert emails and even suspend the login if failures exceed a threshold.
Edit
Similar to my answer to question Spring security 3 : Save informations about authentification in database, I think that capturing an authentication failure event (as opposed to customizing a handler) and storing information in database will also work and it will keep the code decoupled as well.
回答4:
As suggested by Rob Winch in http://forum.springsource.org/showthread.php?108640-Login-attempts-Spring-security, I just subclassed DaoAuthenticationProvider
(which could also have been done using an aspect as Ritesh suggests) to limit the number of failed logins, but you could also assert pre-conditions as well:
public class LimitingDaoAuthenticationProvider extends DaoAuthenticationProvider {
@Autowired
private UserService userService;
@Override
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
// Could assert pre-conditions here, e.g. rate-limiting
// and throw a custom AuthenticationException if necessary
try {
return super.authenticate(authentication);
} catch (BadCredentialsException e) {
// Will throw a custom exception if too many failed logins have occurred
userService.recordLoginFailure(authentication);
throw e;
}
}
}
In Spring config XML, simply reference this bean:
<beans id="authenticationProvider"
class="mypackage.LimitingDaoAuthenticationProvider"
p:userDetailsService-ref="userDetailsService"
p:passwordEncoder-ref="passwordEncoder"/>
<security:authentication-manager>
<security:authentication-provider ref="authenticationProvider"/>
</security:authentication-manager>
Note that I think that solutions which rely on accessing an AuthenticationException
's authentication
or extraInformation
properties (such as implementing an AuthenticationFailureHandler
) should probably not be used because those properties are now deprecated (in Spring Security 3.1 at least).
回答5:
You could also use a service which implements ApplicationListener<AuthenticationFailureBadCredentialsEvent> to update the record in DB.
See spring application events.
回答6:
Here is my implementation, hope help.
- Create a table to store any invalid login attempts.
- If invalid attempts > max allowed, set UserDetail.accountNonLocked to false
- Spring Security will handle the "lock process" for you. (refer to
AbstractUserDetailsAuthenticationProvider
)
Last, extends DaoAuthenticationProvider, and integrate the logic inside.
@Component("authenticationProvider")
public class YourAuthenticationProvider extends DaoAuthenticationProvider {
@Autowired
UserAttemptsDao userAttemptsDao;
@Override
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
try {
Authentication auth = super.authenticate(authentication);
//if corrent password, reset the user_attempts
userAttemptsDao.resetFailAttempts(authentication.getName());
return auth;
} catch (BadCredentialsException e) {
//invalid login, update user_attempts, set attempts+1
userAttemptsDao.updateFailAttempts(authentication.getName());
throw e;
}
}
}
For full source code and implementation, please refer to this - Spring Security limit login attempts example,
回答7:
- create a table to store the values of failed attempts ex : user_attempts
Write custom event listener
@Component("authenticationEventListner") public class AuthenticationEventListener implements AuthenticationEventPublisher { @Autowired UserAttemptsServices userAttemptsService; @Autowired UserService userService; private static final int MAX_ATTEMPTS = 3; static final Logger logger = LoggerFactory.getLogger(AuthenticationEventListener.class); @Override public void publishAuthenticationSuccess(Authentication authentication) { logger.info("User has been logged in Successfully :" +authentication.getName()); userAttemptsService.resetFailAttempts(authentication.getName()); } @Override public void publishAuthenticationFailure(AuthenticationException exception, Authentication authentication) { logger.info("User Login failed :" +authentication.getName()); String username = authentication.getName().toString(); UserAttempts userAttempt = userAttemptsService.getUserAttempts(username); User userExists = userService.findBySSO(username); int attempts = 0; String error = ""; String lastAttempted = ""; if (userAttempt == null) { if(userExists !=null ){ userAttemptsService.insertFailAttempts(username); } } else { attempts = userAttempt.getAttempts(); lastAttempted = userAttempt.getLastModified(); userAttemptsService.updateFailAttempts(username, attempts); if (attempts + 1 >= MAX_ATTEMPTS) { error = "User account is locked! <br>Username : " + username+ "<br>Last Attempted on : " + lastAttempted; throw new LockedException(error); } } throw new BadCredentialsException("Invalid User Name and Password"); } }
3.Security Configuration
1) @Autowired
@Qualifier("authenticationEventListner")
AuthenticationEventListener authenticationEventListner;
2) @Bean
public AuthenticationEventPublisher authenticationListener() {
return new AuthenticationEventListener();
}
3) @Autowired
public void
configureGlobalSecurity(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
//configuring custom user details service
auth.authenticationProvider(authenticationProvider);
// configuring login success and failure event listener
auth.authenticationEventPublisher(authenticationEventListner);
}