1、经过参考一些文档整合了,校验密码输入错误次数及锁定多久的时间设置。希望能帮助有需要的朋友。好用请拿走,给个好评谢谢!
package com.etop.shiro;
import java.sql.Date;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Map;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.atomic.AtomicInteger;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.ExcessiveAttemptsException;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* 验证器,增加了登录次数校验及过期时间设置功能
* @author WangBenYan
*/
@SuppressWarnings("unused")
public class RetryLimitCredentialsMatcher extends HashedCredentialsMatcher {
private static final Logger log = LoggerFactory.getLogger(RetryLimitCredentialsMatcher.class);
// 集群中可能会导致出现验证多过5次的现象,因为AtomicInteger只能保证单节点并发
private Cache<String, AtomicInteger> lgoinRetryCache;
// 缓存用户的错误次数及过期时间
private Cache<String, CacheData> loginCache;
private int maxRetryCount = 2;
private String lgoinRetryCacheName;
private CacheData errorCount;
private static SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
//当前缓存时间
private static String currentdate ;
// 过期时间(单位,毫秒)
private long expireTime = 300000L;
public void setMaxRetryCount(int maxRetryCount) {
this.maxRetryCount = maxRetryCount;
}
public RetryLimitCredentialsMatcher(CacheManager cacheManager, String lgoinRetryCacheName) {
this.lgoinRetryCacheName = lgoinRetryCacheName;
loginCache = cacheManager.getCache(lgoinRetryCacheName);
}
@Override
public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
String username = (String) token.getPrincipal();
// errorCount count + 1
errorCount = loginCache.get(username);
AtomicInteger tempCount = null;
if (null == errorCount) {
tempCount = new AtomicInteger(0);
// 记录缓存次数及过期时间
errorCount = new CacheData(tempCount, expireTime);
loginCache.put(username, errorCount);
currentdate = sf.format(new Date(Calendar.getInstance().getTimeInMillis()));
}
if (errorCount.data.incrementAndGet() >= maxRetryCount) {
log.info("username: " + username + " 因用户连续输错密码次数超过{}次被锁定,请5分钟后再登录,当前登录次数:{}", maxRetryCount,errorCount.data);
/* 启动定时任务清理过期缓存,避免内存溢出 */
Timer t = new Timer();
/* 一分钟调度一次 */
log.info(" **************** 开启定时清理过期的缓存任务 ,当前任务名称:{} **************** ", username);
t.schedule(new ClearTimerTask(loginCache), 0, 60 * 1000);
throw new ExcessiveAttemptsException("username: " + username + " tried to login more than 5 times in period");
}
boolean matches = super.doCredentialsMatch(token, info);
if (matches) {
// clear cache data
loginCache.remove(username);
}
return matches;
}
/**
* 清理过期数据定时任务
* @author WangBenYan
*/
private static class ClearTimerTask extends TimerTask {
Cache<String, CacheData> cache;
public ClearTimerTask(Cache<String, CacheData> cache) {
this.cache = cache;
}
@Override
public void run() {
Set<String> keys = cache.keys();
log.info("ClearTimerTask run......{}", keys.toString());
CacheData data;
for (String key : keys) {
data = cache.get(key);
String overTime = sf.format(new Date(data.expireTime));
if (data.expireTime <= 0) {
log.info(" **************** 缓存永不过期,缓存数据:" + key + " ,错误次数:" + data.data + " ,过期时间:" + overTime+ " ,缓存时间: " + currentdate);
continue;
}
/* 到期时间 > 当前时间,则继续 */
if (data.expireTime > Calendar.getInstance().getTimeInMillis()) {
log.info(" **************** 缓存的用户 :" + key + " ,错误次数:" + data.data + " ,过期时间:" + overTime+ " ,缓存时间: " + currentdate);
continue;
}
log.info(" **************** 开始清除缓存 **************** : " + key);
cache.remove(key);
log.info(" **************** 定时清理过期的缓存... {} ", cache.get(key));
this.cancel();
}
}
}
private static class CacheData {
// 缓存数据
private AtomicInteger data;
// 过期时间(单位,毫秒)
private long expireTime;
public CacheData(AtomicInteger errorcount, long expire) {
log.info(" **************** 缓存认证错误次数:" + errorcount + " 过期时间毫秒数:" + expire);
this.data = errorcount;
if (expire <= 0) {
this.expireTime = 0L;
} else {
this.expireTime = Calendar.getInstance().getTimeInMillis() + expire;
}
}
}
}