spring security中限制用户登录次数超过限制的处理 - jackyrong - ITeye博客

在登录的时候,往往希望记录如果登录失败者的ip,并且登录失败次数超过一定的,则不给登录,予以封锁。在spring security中,可以通过如下方式实现。

1) 实现AuthenticationFailureEventListener,这个监听器用来监听
登录失败的事件。

  

Java代码  收藏代码 "收藏这段代码")

  1. @Component
  2. publicclass AuthenticationFailureListener   
  3.   implements ApplicationListener<AuthenticationFailureBadCredentialsEvent> {  
  4.     @Autowired
  5.     private LoginAttemptService loginAttemptService;  
  6.     publicvoid onApplicationEvent(AuthenticationFailureBadCredentialsEvent e) {  
  7.         WebAuthenticationDetails auth = (WebAuthenticationDetails)   
  8.           e.getAuthentication().getDetails();  
  9.         loginAttemptService.loginFailed(auth.getRemoteAddress());  
  10.     }  
  11. }  

@Component
public class AuthenticationFailureListener
implements ApplicationListener<AuthenticationFailureBadCredentialsEvent> {

@Autowired
private LoginAttemptService loginAttemptService;

public void onApplicationEvent(AuthenticationFailureBadCredentialsEvent e) {
    WebAuthenticationDetails auth = (WebAuthenticationDetails) 
      e.getAuthentication().getDetails();
     
    loginAttemptService.loginFailed(auth.getRemoteAddress());
}

}

2) 同样有登录成功的监听
AuthenticationSuccessEventListener,
  

Java代码  收藏代码 "收藏这段代码")

  1. @Component
  2. publicclass AuthenticationSuccessEventListener   
  3.   implements ApplicationListener<AuthenticationSuccessEvent> {  
  4.     @Autowired
  5.     private LoginAttemptService loginAttemptService;  
  6.     publicvoid onApplicationEvent(AuthenticationSuccessEvent e) {  
  7.         WebAuthenticationDetails auth = (WebAuthenticationDetails)   
  8.           e.getAuthentication().getDetails();  
  9.         loginAttemptService.loginSucceeded(auth.getRemoteAddress());  
  10.     }  
  11. }  

@Component
public class AuthenticationSuccessEventListener
implements ApplicationListener<AuthenticationSuccessEvent> {

@Autowired
private LoginAttemptService loginAttemptService;

public void onApplicationEvent(AuthenticationSuccessEvent e) {
    WebAuthenticationDetails auth = (WebAuthenticationDetails) 
      e.getAuthentication().getDetails();
     
    loginAttemptService.loginSucceeded(auth.getRemoteAddress());
}

}

3) LoginAttemptService中,主要是通过缓存记录登录失败次数等,十分简单
  

Java代码  收藏代码 "收藏这段代码")

  1. @Service
  2. publicclass LoginAttemptService {  
  3.     privatefinalint MAX_ATTEMPT = 10;  
  4.     private LoadingCache<String, Integer> attemptsCache;  
  5.     public LoginAttemptService() {  
  6.         super();  
  7.         attemptsCache = CacheBuilder.newBuilder().  
  8.           expireAfterWrite(1, TimeUnit.DAYS).build(new CacheLoader<String, Integer>() {  
  9.             public Integer load(String key) {  
  10.                 return0;  
  11.             }  
  12.         });  
  13.     }  
  14.     publicvoid loginSucceeded(String key) {  
  15.         attemptsCache.invalidate(key);  
  16.     }  
  17.     publicvoid loginFailed(String key) {  
  18.         int attempts = 0;  
  19.         try {  
  20.             attempts = attemptsCache.get(key);  
  21.         } catch (ExecutionException e) {  
  22.             attempts = 0;  
  23.         }  
  24.         attempts++;  
  25.         attemptsCache.put(key, attempts);  
  26.     }  
  27.     publicboolean isBlocked(String key) {  
  28.         try {  
  29.             return attemptsCache.get(key) >= MAX_ATTEMPT;  
  30.         } catch (ExecutionException e) {  
  31.             returnfalse;  
  32.         }  
  33.     }  
  34. }  

@Service
public class LoginAttemptService {

private final int MAX_ATTEMPT = 10;
private LoadingCache<String, Integer> attemptsCache;

public LoginAttemptService() {
    super();
    attemptsCache = CacheBuilder.newBuilder().
      expireAfterWrite(1, TimeUnit.DAYS).build(new CacheLoader<String, Integer>() {
        public Integer load(String key) {
            return 0;
        }
    });
}

public void loginSucceeded(String key) {
    attemptsCache.invalidate(key);
}

public void loginFailed(String key) {
    int attempts = 0;
    try {
        attempts = attemptsCache.get(key);
    } catch (ExecutionException e) {
        attempts = 0;
    }
    attempts++;
    attemptsCache.put(key, attempts);
}

public boolean isBlocked(String key) {
    try {
        return attemptsCache.get(key) >= MAX_ATTEMPT;
    } catch (ExecutionException e) {
        return false;
    }
}

}

  
4) 最后在userdetailService类中,修改下
 

Java代码  收藏代码 "收藏这段代码")

  1. @Service("userDetailsService")  
  2. @Transactional
  3. publicclass MyUserDetailsService implements UserDetailsService {  
  4.     @Autowired
  5.     private UserRepository userRepository;  
  6.     @Autowired
  7.     private RoleRepository roleRepository;  
  8.     @Autowired
  9.     private LoginAttemptService loginAttemptService;  
  10.     @Autowired
  11.     private HttpServletRequest request;  
  12.     @Override
  13.     public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {  
  14.         String ip = request.getRemoteAddr();  
  15.         if (loginAttemptService.isBlocked(ip)) {  
  16.             thrownew RuntimeException("blocked");  
  17.         }  
  18.         try {  
  19.           ................  
  20. }  

@Service("userDetailsService")
@Transactional
public class MyUserDetailsService implements UserDetailsService {

@Autowired
private UserRepository userRepository;

@Autowired
private RoleRepository roleRepository;

@Autowired
private LoginAttemptService loginAttemptService;

@Autowired
private HttpServletRequest request;


@Override
public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException {
    String ip = request.getRemoteAddr();
    if (loginAttemptService.isBlocked(ip)) {
        throw new RuntimeException("blocked");
    }

    try {
      ................

}

5) 注意这里修改下web.xml,因为loadUserByUsername中要获得ip
  

<listener>
    <listener-class>
        org.springframework.web.context.request.RequestContextListener
    </listener-class>
</listener>
6) 前端jsp页面适当修改
   <c:when test="${SPRING_SECURITY_LAST_EXCEPTION.message == 'blocked'}">
            <div class="alert alert-error">
                <spring:message code="auth.message.blocked"></spring:message>
            </div>
        </c:when>


Original url: Access
Created at: 2019-06-24 12:27:35
Category: default
Tags: none

请先后发表评论
  • 最新评论
  • 总共0条评论