在Spring Security中,是否有一些配置或可用模块来限制登录尝试次数(理想情况下,我希望在连续失败的尝试之间有逐渐增加的等待时间)?如果没有,应使用API的哪个部分进行此操作?
在Spring Security中,是否有一些配置或可用模块来限制登录尝试次数(理想情况下,我希望在连续失败的尝试之间有逐渐增加的等待时间)?如果没有,应使用API的哪个部分进行此操作?
从Spring 4.2开始,可以使用基于注解的事件监听器:
@Component
public class AuthenticationEventListener {
@EventListener
public void authenticationFailed(AuthenticationFailureBadCredentialsEvent event) {
String username = (String) event.getAuthentication().getPrincipal();
// update the failed login count for the user
// ...
}
}
实现一个AuthenticationFailureHandler,它在数据库中更新计数/时间。我不会依赖于使用会话,因为攻击者不会发送cookie。
编辑
与我对“Spring Security 3:将有关认证的信息保存到数据库中”问题的回答类似,我认为捕获身份验证失败事件(而不是自定义处理程序)并将信息存储在数据库中也可以解决问题,且代码保持解耦。
正如Rob Winch在http://forum.springsource.org/showthread.php?108640-Login-attempts-Spring-security中所建议的那样,我只是通过子类化DaoAuthenticationProvider
(也可以像Ritesh建议的那样使用切面)来限制失败登录次数,但您也可以预先断言条件:
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;
}
}
}
<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>
AuthenticationException
的authentication
或extraInformation
属性的解决方案(例如实现AuthenticationFailureHandler
)可能不应该使用,因为这些属性现在已被弃用(至少在Spring Security 3.1中)。这是我的实现,希望有所帮助。
AbstractUserDetailsAuthenticationProvider
)。最后,扩展DaoAuthenticationProvider,并将逻辑集成在内。
@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;
}
}
}
完整源代码和实现请参考 - Spring Security登录尝试次数限制示例,
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");
}
}
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);
}