Java接口防刷策略(自定义注解实现)

前言

本文一定要看完,前部分为逻辑说明及简单实现,文章最后有最终版解决方案(基于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()
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值