`

spring security中限制用户登录次数超过限制的处理

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

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

  
@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,
  
@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中,主要是通过缓存记录登录失败次数等,十分简单
  
@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类中,修改下
 
@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>
1
1
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics