2011-03-18 101 views

回答

12

实现一个AuthenticationFailureHandler,用于更新数据库中的计数/时间。我不会指望使用会话,因为攻击者无论如何都不会发送cookie。

+4

不幸的是,由于getAuthentication和getExtraInformation都不再使用AuthenticationException,所以无法从数据库获取用户(不解析HttpServletRequest的参数),因此很难获得Principal(用于其用户名或ID)。使用AuthenticationProvider似乎适用于(下面,类似于Ritesh的建议)。 – paulcm 2013-03-26 18:32:16

4

我最近实现了一个类似的功能来监视使用JMX的登录失败。请参阅我对Publish JMX notifications in using Spring without NotificationPublisherAware问题的回答中的代码。身份验证提供程序的身份验证方法的一个方面是更新MBean,并与通知侦听程序(代码未在该问题中显示)一起使用来阻止用户和IP,发送警报电子邮件,甚至在失败超过阈值时挂起登录。

编辑
到我的回答质疑Spring security 3 : Save informations about authentification in database类似,我认为拍摄认证失败事件(而不是定制处理器),并存储在数据库中的信息也将工作,它会保持分离以及代码。

5

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; 
     } 
    } 
} 

在Spring XML配置,简单地引用这个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> 

注意,我认为解决方案依赖于访问的AuthenticationExceptionauthentication或可能不应该使用属性(例如实现AuthenticationFailureHandler),因为这些属性现在已被弃用(至少在Spring Security 3.1中)。

3

您也可以使用实现ApplicationListener <AuthenticationFailureBadCredentialsEvent>的服务来更新数据库中的记录。

查看春季应用程序事件。

+1

我发现这种方式比创建自定义身份验证提供程序的其他解决方案更清洁。从spring 4.2开始,这可以通过批注来处理,从而进一步将类与Spring Security框架分离。 – Jberg 2015-07-27 15:38:24

3

这是我的实施,希望有所帮助。

  1. 创建一个表来存储任何无效的登录尝试。
  2. 如果无效次数> max允许,将UserDetail.accountNonLocked设置为false
  3. Spring Security将为您处理“锁定过程”。 (参考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 limit login attempts example

9

从春天4。2个向上annotation based event listeners可供选择:

@Component 
public class AuthenticationEventListener { 

    @EventListener 
    public void authenticationFailed(AuthenticationFailureBadCredentialsEvent event) { 

     String username = (String) event.getAuthentication().getPrincipal(); 

     // update the failed login count for the user 
     // ... 
    } 

} 
1
  1. 创建一个表来存储失败的尝试前值:user_attempts
  2. 编写自定义事件侦听器

    @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配置

  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); 
     } 
+0

无需抛出BadCredentialsException - 它将由事件发布者(例如ProviderManager)抛出。 – isobretatel 2017-05-04 16:57:13

+0

听众方法存在问题:您没有真正锁定用户帐户。下次用户输入有效凭证时,系统应显示错误消息“您的帐户被锁定”,但实际上它将允许成功验证。 – isobretatel 2017-05-04 17:08:40

+0

在侦听器中抛出异常并不是一个好习惯。 – isobretatel 2017-05-05 12:40:23