搜索
简帛阁>技术文章>springboot项目应用ehcahe-shiro限制短信登录失败次数

springboot项目应用ehcahe-shiro限制短信登录失败次数

springboot项目应用ehcahe-shiro限制短信登录失败次数

  • 需求背景
  • 实现思路
  • 技术选择
  • 实现代码
        • 1、在 main/resources 下新建 config 文件夹,在 config 中新建 ehcache-shiro.xml 文件,内容如下:
        • 2、在校验验证码的方法 smsServiceImpl.java 中,写入内容如下:
        • 3、在登录方法中,调用上述校验结果判断即可:

需求背景

用户使用手机号和短信验证码登录,项目要求限制用户登录失败的次数,即失败超过6次,就锁定用户账号。

实现思路

以登陆者手机号码 + “errorTimes” 为 key 值进行记录。验证码输入错误后记录次数,验证码输入错误次数达到 6 次以后,将该用户状态设置为锁定。

技术选择

1、MySql 新增缓存表进行记录;
2、使用 ehcache 进行缓存;
3、使用 redis 进行缓存;
进过比较,ehcache 比较契合我们项目的现状,故此选择。
(在网上找到了相关限制登录次数的方法,单都是账号密码登录。取得是数据库中密码而非短信验证码,所以只能自己写了)

实现代码

1、在 main/resources 下新建 config 文件夹,在 config 中新建 ehcache-shiro.xml 文件,内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<ehcache name="es">

    <diskStore path="java.io.tmpdir"/>

    <!--
       name:缓存名称。
       maxElementsInMemory:缓存最大数目
       maxElementsOnDisk:硬盘最大缓存个数。
       eternal:对象是否永久有效,一但设置了,timeout将不起作用。
       overflowToDisk:是否保存到磁盘,当系统当机时
       timeToIdleSeconds:设置对象在失效前的允许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。
                TTI用于设置对象在cache中的最大闲置时间,就是 在一直不访问这个对象的前提下,这个对象可以在cache中的存活时间。
       timeToLiveSeconds:设置对象在失效前允许存活时间(单位:秒)。最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,默认是0.,也就是对象存活时间无穷大。
             TTL用于设置对象在cache中的最大存活时间,就是 无论对象访问或是不访问(闲置),这个对象在cache中的存活时间。
       diskPersistent:是否缓存虚拟机重启期数据 Whether the disk store persists between restarts of the Virtual Machine. The default value is false.
       diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。
       diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。
       memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。
        clearOnFlush:内存数量最大时是否清除。
         memoryStoreEvictionPolicy:
            Ehcache的三种清空策略;
            FIFO,first in first out,这个是大家最熟的,先进先出。
            LFU, Less Frequently Used,就是上面例子中使用的策略,直白一点就是讲一直以来最少被使用的。如上面所讲,缓存的元素有一个hit属性,hit值最小的将会被清出缓存。
            LRU,Least Recently Used,最近最少使用的,缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存。
    -->
    <defaultCache
            maxElementsInMemory="10000"
            eternal="false"
            timeToIdleSeconds="120"
            timeToLiveSeconds="120"
            overflowToDisk="false"
            diskPersistent="false"
            diskExpiryThreadIntervalSeconds="120"
    />


    <!-- 登录错误记录缓存锁定24小时 -->
    <cache name="errorLoginCache"
           maxEntriesLocalHeap="2000"
           eternal="false"
           timeToIdleSeconds="0"
           timeToLiveSeconds="86400"
           overflowToDisk="false"
           statistics="true">
    </cache>

</ehcache>

。java

2、在校验验证码的方法 smsServiceImpl.java 中,写入内容如下:

(验证码对比方法:前端传过来的验证码和发送短信后存储在 session 中的验证码对比)

	@Override
    public boolean verifySms(String mobile, String code, HttpSession httpSession) {
        // 获取 session 验证码
        String sessionString = (String) httpSession.getAttribute("sms_login_" + mobile);
        //1,创建缓存管理器
        CacheManager cm = CacheManager.create(this.getClass().getResourceAsStream("/config/ehcache-shiro.xml"));
        //2,获取指定的缓存
        Cache cache = cm.getCache("errorLoginCache");
        Element element  = cache.get(mobile+"errorTimes");
        if (sessionString != null) {
            // 字符串前四位是验证码,后面是创建时间
            String sessionCode = sessionString.substring(0, 4);
            // 比较验证码是否一致  return sessionCode.equals(code);
            Integer count = 0;
            if (!sessionCode.equals(code)){
                //4,判断数据
                if (element == null) {
                    //如果用户没有登陆过,登陆次数加1 并放入缓存
                    cache.put(new Element(mobile+"errorTimes", 1));
                } else {
                    count = (Integer) element.getObjectValue();
                    if (count > 4){ // 从0到4, 点击6次锁定,所以需要大于4
                        SysEmployee sysEmployee = sysEmployeeService.findByMobile(mobile);
                        if (sysEmployee != null && sysEmployee.getStatus() != -1 ){
                            //修改数据库的状态字段为锁定
                            sysEmployee.setStatus(-1);
                            sysEmployeeService.update(sysEmployee);
                        }
                    }else{
                        cache.put(new Element(mobile+"errorTimes", count+1));
                    }

                }
            }else{
                // 登录成功,删除缓存中的登录错误次数
                cache.remove(mobile+"errorTimes");
                return true;
            }
        }
        return false;
    }

3、在登录方法中,调用上述校验结果判断即可:

 if (smsService.verifySms(mobile, code, httpSession)){
        try {
            Subject subject = getSubject();
            if (subject != null)
                subject.logout();
            subject.login(token);
            // 更新登陆记录
            sysEmployeeService.saveLoginInfo(getCurrentUser());
            return "redirect:/";
        } catch (UnknownAccountException | IncorrectCredentialsException | LockedAccountException e) {
            attributes.addFlashAttribute("type", "error");
            attributes.addFlashAttribute("msg", e.getMessage());
            attributes.addFlashAttribute("mobile", mobile);
            return "redirect:/login";
        } catch (AuthenticationException e) {
            attributes.addFlashAttribute("type", "error");
            attributes.addFlashAttribute("msg", "认证失败");
            attributes.addFlashAttribute("mobile", mobile);
            return "redirect:/login";
        }
    }else{
        if (sysEmployee.getStatus() != -1){
            attributes.addFlashAttribute("type", "error");
            attributes.addFlashAttribute("msg", "验证码错误");
            attributes.addFlashAttribute("mobile", mobile);
        }else{
            attributes.addFlashAttribute("type", "error");
            attributes.addFlashAttribute("msg", "账号已被锁定,请联系管理员");
            attributes.addFlashAttribute("mobile", mobile);
        }
        return "redirect:/login";
    }

第一下次使用 ehcache,难免有错误疏漏 ,如有不足还请指正。

这次讲讲如何限制用户登录尝试次数,防止坏人多次尝试,恶意暴力破解密码的情况出现,要限制用户登录尝试次数,必然要对用户名密码验证失败做记录,Shiro中用户名密码的验证交给了CredentialsMat
/***认证信息(身份验证):Authentication是用来验证用户身份**@paramtoken*@return*@throwsAuthenticationException*/@Overrid
思路下面是我以前写的代码,没考虑高并发场景。如果是高并发场景下,要考虑到redis的set方法覆盖值问题,可以使用incr来替代get,set保证数据安全通过redis记录登录失败次数,以用户的u
在用户身份验证的情况下,Laravel具有内置的身份验证系统。我们可以根据要求轻松修改它。身份验证中包含的功能之一是Throttling为什么我们需要throttling保护?基本上,throttli
当用户连续登录失败次数过多时,Oracle会锁定该用户,“FAILED_LOGIN_ATTEMPTS”用于设置最大次数,超过该值则锁定该帐号。要取消用户连续登录失败次数限制可以按照以下方法操作:1输
1、springboot+Shiro+登录2、引入相关支持<dependency><groupId>orgapacheshiro</groupId><art
pam_tally2模块(方法一)用于对系统进行失败的ssh登录尝试后锁定用户帐户。此模块保留已尝试访问的计数和过多的失败尝试。[root@zabbix~]vim/etc/pamd/login%PA
当用户连续登录失败次数过多时,Oracle会锁定该用户,“FAILED_LOGIN_ATTEMPTS”用于设置最大次数,超过该值则锁定该帐号。要取消用户连续登录失败次数限制可以按照以下方法操作:1输
一、创建一个实现AccessControlFilter的过滤器类packagecombeovodsdcommonshirofilter;importcnhutoolcorecollectionColl
一般的登录流程会有:用户名不存在,密码错误,验证码错误等在集成shiro后,应用程序的外部访问权限以及访问控制交给了shiro来管理。shiro提供了两个主要功能:认证(Authentication