Aspect实现接口调用频率限制
aop 的思想,就是讲方法外到内执行,中间给切成三段,未进入,进入,已退出
背景:
最近在做一个毕业设计,需要发送邮件(发送密码重置验证码),为了避免用户频繁调用邮件发送接口。
前端:将按钮置灰,开启倒计时,倒计时结束恢复按钮可用
由于没有登录,恶意的可能会使用代码去刷接口(在未进入时将其拦截处理)
后端:
使用自定义注解+aop 通过用户ip 限制短时间内的访问次数
准备:
自定注解,可以配置多长时间内最多访问多少次
aspect:处理标注了自定义注解的参数,执行相关逻辑。
代码实现
注解:
@Documented
@Target({ ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface RequestFrequencyLimit {
//访问时间间隔 默认是60 秒
int interval() default 60000;
//访问次数限制
int count() default 3;//默认interval 时间内最多访问3次
}
aspect:
/** * 接口访问频率 限制aop */
@Aspect
@Component
@Slf4j
public class RequestFrequencyLimitAop {
Map<String, RequestRecord> records = new ConcurrentHashMap<>();//由于是单例,需要注意线程安全,当然也可以使用redis 替代
@Autowired
HttpServletRequest request;
@Pointcut("@annotation(top.forethought.orderfoodyoulike.aops.RequestFrequencyLimit)")
public void requestLimit() {
}
@Around("requestLimit() && @annotation(requestFrequencyLimit)")
public Object doAround(ProceedingJoinPoint joinPoint, RequestFrequencyLimit requestFrequencyLimit) throws Throwable {
Object res = null;
long now = System.currentTimeMillis();
if (isNull(requestFrequencyLimit)) {
return joinPoint.proceed();
}
//根据ip查找访问记录
String clientIp = IpUtil.getIpAddress(request);
RequestRecord requestRecord = records.get(clientIp);
if (isNull(requestRecord) || now - requestRecord.firstRequestTime > requestFrequencyLimit.interval()) {
requestRecord = new RequestRecord();
requestRecord.firstRequestTime = now;
requestRecord.requestCount = 1;
} else {
//更新访问次数
requestRecord.requestCount++;
}
if (requestRecord.requestCount > requestFrequencyLimit.count()) {
log.error("访问太过频繁,请稍后再试,ip={}", clientIp);
throw GlobalException.builder().msg("访问太过频繁,请稍后再试").build();
}
//保存访问记录
records.put(clientIp, requestRecord);
return joinPoint.proceed();
}
class RequestRecord {
int requestCount;//访问次数
Long firstRequestTime;//第一次访问时间
}
}
使用效果:
60秒内同一个ip 最多访问该接口3次
还没有评论,来说两句吧...