使用自定义注解,并利用AOP切入注解的方法,判断当前登录人登录的次数,如果连续输入密码错误超过N次,则禁止该用户登录一段时间。
代码如下
首先是自定义注解
/**
* 限制登录次数
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Limit {
/**
* 名称,自定义使用位置,可用来区分
*/
String name() default "";
/**
* 资源的key
*/
String key() default "";
/**
* 前缀
*/
String prefix() default "login_limit_";
/**
* 默认限制次数
* 例:用在登录上即限制三次登录失败,就锁定账号一段时间
*/
int count() default 3;
/**
* 默认限制时间,半个小时之内不能解除封印
*/
int period() default 1800;
/**
* 限制类型
*/
SysEnum.LimitType limitType() default SysEnum.LimitType.IP;
}
然后是具体的AOP逻辑
/**
* <p>
* @description: 注解Limit的限制
* </p>
* @author: ZengGuangfu
*/
@Slf4j
@Aspect
@Component
public class LimitHandler {
@Autowired
RedisAuxiliary redisAuxiliary;
@Pointcut("@annotation(com.emperor.go.config.annocation.Limit)")
public void cut(){ }
@Around("cut()")
public Object around(ProceedingJoinPoint point) throws Throwable {
MethodSignature methodSignature = (MethodSignature) point.getSignature();
Object[] args = point.getArgs();
Limit limit = methodSignature.getMethod().getAnnotation(Limit.class);
Object result = null;
//获取request
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
if (Objects.nonNull(limit)){
// 获取参数
String key = limit.key();
String name = limit.name();
String prefix = limit.prefix();
int period = limit.period();
int count = limit.count();
SysEnum.LimitType limitType = limit.limitType();
StringBuffer redisKey = new StringBuffer("prefix_");
LoginVO lv = null;
if (args != null && args.length > 0 && args[0] instanceof LoginVO){
lv = (LoginVO) args[0];
}
switch (limitType){
case IP:
redisKey.append(IPUtil.getIpAddr(request) + ":");
break;
case SPECIAL:
if (Objects.nonNull(lv)){
redisKey.append(key).append("_" + lv.getLoginName() + ":");
}
break;
}
String currentKey = redisKey.toString();
/**
* 逻辑更改:
* 1.先查询redis有没有该记录,如果有,且数字大于 count 限制,即不能再登录
* 2.根据结果判断,如果登录成功,则清除缓存限制信息。如果失败了,则数字加一
*/
try{
int anInt = 0;
Long number = redisAuxiliary.getLong(currentKey);
if (Objects.nonNull(number)){
anInt = number.intValue();
}
if (anInt > count){
throw new GlobalException(CodeMsg.LOGIN_TOO_MUCH);
}
result = point.proceed();
if (Objects.nonNull(result) && result instanceof String){
if (result.equals("redirect:/main.html")){
redisAuxiliary.delete(currentKey);
}else if (result.equals("login")){
redisAuxiliary.incrByExpireTime(currentKey, 1800);
}
}
}catch (NumberFormatException e){
throw new GlobalException(CodeMsg.RUNTIME_ERROR.fillMsg("Limit AOP 数字转换异常,可能是缓存存储数据不是数字"));
}catch (GlobalException e){
throw e;
}catch (Exception e){
throw e;
}
}
return result;
}
}
仅供参考
网友评论