前言
本文一定要看完,前部分为逻辑说明及简单实现,文章最后有最终版解决方案(基于lua脚本),因为前部分是防君子不防小人,无法抵挡for循环调用。
目的
- 短信发送及短信验证码校验接口防刷
一方面防止用户循环调用刷短信验证码
另一方面防止用户循环调用测短信验证码(一般短信验证码为6位纯数字,一秒钟上百次调用,如果不做限制很快就能试出来了) - 很多接口需要防止前端重复调用
误操作多次点击,不属于攻击类型,正常用户经常会触发的,例如信息发布可能前端限制未做好,误点击了多次,这种情况实际上应该只记录第一次的,后续的不应该继续操作数据库。 - 极端的情况
可能很多接口一天或者很长时间只能调用一次(类似签到?个人想法是尽量不让数据到了数据库层再抛异常)
解决措施
利用Spring AOP理念,自定义注解实现接口级访问次数限制
访问次数记录使用Redis存储,Redis的过期机制很适合当前场景,而且可以在更大程度上提升性能
-
定义注解
package com.cong.core.rate; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target({ ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface RateLimit { /** 周期,单位是秒 */ int cycle() default 5; /** 请求次数 */ int number() default 1; /** 默认提示信息 */ String msg() default "请勿重复点击"; }默认是5秒调用一次,现在网上一大堆脚本,贴吧发帖跟帖自动化,实际上打字点击发帖的正常频率也不会超过2秒一次吧,但是机器很容易就超过这个速度了,在一定程度上也可以限制这种情况的发生。
接口级限制,所以当前注解只作用在方法上。 -
定义接口访问频次限制接口
package com.cong.core.rate; public interface RateLimitService { /** * 接口频次限制校验 * * @param ip * 客户端IP * @param uri * 请求接口名 * @param rateLimit * 限制频次信息 * @return * @author single-聪 * @date 2020年6月1日 * @version 1.6.1 */ Boolean limit(String ip, String uri, RateLimit rateLimit); }因为Interceptor拦截器最终返回值是true或false,所以当前接口返回值为boolean类型。
关于参数,可以设法获取设备Mac地址,对于某些明显是攻击的IP及设备封禁。 -
RateLimitService接口默认实现类
package com.cong.core.rate; import java.util.concurrent.TimeUnit; import org.springframework.data.redis.core.RedisTemplate; import lombok.extern.slf4j.Slf4j; @Slf4j public class DefaultRateLimitServiceImpl implements RateLimitService { private RedisTemplate<String, Integer> redisTemplate; public void setRedisTemplate(RedisTemplate<String, Integer> redisTemplate) { this.redisTemplate = redisTemplate; } @Override public Boolean limit(String ip, String uri, RateLimit rateLimit) { log.info("默认的实现,请自定义实现类覆盖当前实现"); String key = "rate:" + ip + ":" + uri; // 缓存中存在key,在限定访问周期内已经调用过当前接口 if (redisTemplate.hasKey(key)) { // 访问次数自增1 redisTemplate.opsForValue().increment(key, 1); // 超出访问次数限制 if (redisTemplate.opsForValue()

最低0.47元/天 解锁文章
1298

被折叠的 条评论
为什么被折叠?



