Spring Security之屡次登陆失败后帐户锁定功能的实现

file

在上一次写的文章中,为你们说到了如何动态的从数据库加载用户、角色、权限信息,从而实现登陆验证及受权。在实际的开发过程当中,咱们一般会有这样的一个需求:当用户屡次登陆失败的时候,咱们应该将帐户锁定,等待必定的时间以后才能再次进行登陆操做。html

1、基础知识回顾

要实现屡次登陆失败帐户锁定的功能,咱们须要先回顾一下基础知识:前端

  • Spring Security 不须要咱们本身实现登陆验证逻辑,而是将用户、角色、权限信息以实现UserDetails和UserDetailsService接口的方式告知Spring Security。具体的登陆验证逻辑Spring Security 会帮助咱们实现。
  • UserDetails接口中有一个方法叫作isAccountNonLocked()用于判断帐号是否被锁定,也就是说咱们应该经过该方法对应的set方法setAccountNonLocked(false)告知Spring Security该登陆帐户被锁定。
  • 那么应该在哪里判断帐号登陆失败的次数并执行锁定机制呢?固然是咱们以前文章给你们介绍的《自定义登陆成功及失败结果处理》的AuthenticationFailureHandler。

建议您先阅读本文,若是您对本文的实现过程感到迷惑,建议您再翻看本号以前的相关内容。mysql

2、实现屡次登陆失败锁定的原理

通常来讲实现这个需求,咱们须要针对每个用户记录登陆失败的次数nLock和锁定帐户的到期时间releaseTime。具体你是把这2个信息存储在mysql、仍是文件中、仍是redis中等等,彻底取决于你对你所处的应用架构适用性的判断。具体的实现逻辑无非就是:redis

  • 登录失败以后,从存储中将nLock取出来加1。
  • 若是nLock大于登录失败阈值(好比3次),则将nLock=0,而后设置releaseTime为当前时间加上锁定周期。经过setAccountNonLocked(false)告知Spring Security该登陆帐户被锁定。
  • 若是nLock小于等于1,则将nLock再次存起来。
  • 在一个合适的时机,将锁定状态重置为setAccountNonLocked(true)。

这是一种很是典型的实现方式,笔者向你们介绍一款很是有用的开源软件叫作:ratelimitj。这个软件的功能主要是为API访问进行限流,也就是说能够经过制定规则限制API接口的访问频率。那刚好登陆验证接口也是API的一种啊,咱们正好也须要限制它在必定的时间内的访问次数。spring

3、具体实现

首先须要将ratelimitj经过maven坐标引入到咱们的应用里面来。咱们使用的是内存存储的版本,还有redis存储的版本,你们能够根据本身的应用状况选用。sql

<dependency>
            <groupid>es.moki.ratelimitj</groupid>
            <artifactid>ratelimitj-inmemory</artifactid>
            <version>0.4.1</version>
        </dependency>

以后经过继承SimpleUrlAuthenticationFailureHandler ,实现onAuthenticationFailure方法。该实现是针对登陆失败的结果的处理,在咱们以前的文章中已经讲过。数据库

@Component
public class MyAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {

    @Autowired
    UserDetailsManager userDetailsManager;

    //规则定义:1小时以内5次机会,就触发限流行为
    Set<requestlimitrule> rules = 
            Collections.singleton(RequestLimitRule.of(1 * 60, TimeUnit.MINUTES,5)); 
    RequestRateLimiter limiter = new InMemorySlidingWindowRequestRateLimiter(rules);


    @Override
    public void onAuthenticationFailure(HttpServletRequest request,
                                        HttpServletResponse response, 
                                        AuthenticationException exception) 
                                        throws IOException, ServletException {

         String userId = //从request或request.getSession中获取登陆用户名
         //计数器加1,并判断该用户是否已经到了触发了锁定规则
         boolean reachLimit = limiter.overLimitWhenIncremented(userId);

        if(reachLimit){ //若是触发了锁定规则,经过UserDetails告知Spring Security锁定帐户
               user.setAccountNonLocked(false);
               userDetailsManager.updateUser(user);
               SysUser user = (SysUser) userDetailsManager.loadUserByUsername(userId);
        }
        

        
        //此处省略经过response作json或html响应
    }
}
  • 核心实现注意看代码中的注释
  • 代码中的SysUser为UserDetails的实现类,若是不知道如何实现请参考本号以前的文章
  • userDetailsManager被用于管理UserDetails信息,经过改变UserDetails改变Spring Security验证行为。

4、重置锁定状态的时机

user.setAccountNonLocked(true);

重置锁定状态很简单,就是上面的代码。可是更重要的是如何选择重置锁定状态的时机。笔者能想到几种方案以下json

  • 下一次登录的时候,自定义过滤器,加在Spring Boot过滤器链最前端作锁定状态重置的判断。
  • 当登陆帐户被锁定以后,以后用户的每一次登陆都会抛出LockedException。咱们彻底能够经过Spring Boot的全局异常捕获机制,在其中捕获LockedException,并作锁定状态的判断及重置行为。
  • 写一个Spring 的定时器轮询,固然这是最差的方案。

期待您的关注

相关文章
相关标签/搜索