用户使用手机号和短信验证码登录,项目要求限制用户登录失败的次数,即失败超过6次,就锁定用户账号。
以登陆者手机号码 + “errorTimes” 为 key 值进行记录。验证码输入错误后记录次数,验证码输入错误次数达到 6 次以后,将该用户状态设置为锁定。
1、MySql 新增缓存表进行记录;
2、使用 ehcache 进行缓存;
3、使用 redis 进行缓存;
进过比较,ehcache 比较契合我们项目的现状,故此选择。
(在网上找到了相关限制登录次数的方法,单都是账号密码登录。取得是数据库中密码而非短信验证码,所以只能自己写了)
<?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
(验证码对比方法:前端传过来的验证码和发送短信后存储在 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; }
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,难免有错误疏漏 ,如有不足还请指正。