springboot+redis限制ip访问接口次数

1.maven导入坐标

		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-aspects</artifactId>
		</dependency>

2.定义注解

import java.lang.annotation.*;
 
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
 

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
@Order(Ordered.HIGHEST_PRECEDENCE)
public @interface RequestLimit {
 
	/**
     * 允许访问的最大次数
     */
    int count() default Integer.MAX_VALUE;
 
    /**
     * 时间段,单位为毫秒,默认值一分钟
     */
    long time() default 60000;
}

3.定义异常类

public class RequestLimitException extends Exception{
 
	private static final long serialVersionUID = 1555967171104727461L;
	
	public RequestLimitException(){
        super("HTTP请求超出设定的限制");
    }
 
    public RequestLimitException(String message){
        super(message);
    }
 
}

4. 业务逻辑,存入redis路径,设置过期时间

import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.TimeUnit;

@Aspect
@Component
public class RequestLimitContract {

    private static final Logger logger = LoggerFactory.getLogger(RequestLimitContract.class);
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    private static String limitPath = "/safeLimit/limit";

    @Before("@annotation(limit)")
    public void requestLimit(final JoinPoint joinPoint, RequestLimit limit) throws RequestLimitException {
        try {
            Object[] args = joinPoint.getArgs();
            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
            ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            HttpServletResponse response = servletRequestAttributes.getResponse();
            for (int i = 0; i < args.length; i++) {
                if (args[i] instanceof HttpServletRequest) {
                    request = (HttpServletRequest) args[i];
                    break;
                }
            }
            if (request == null) {
                throw new RequestLimitException("方法中缺失HttpServletRequest参数");
            }
            String ip = request.getRemoteAddr();
            String url = request.getRequestURL().toString();
            String key = "req_limit_".concat(url).concat(ip);
            if (!redisTemplate.hasKey(key) || StringUtils.isEmpty(redisTemplate.opsForValue().get(key))) {
                redisTemplate.opsForValue().set(key, String.valueOf(1), limit.time(), TimeUnit.MILLISECONDS);
            } else {
                Integer getValue = Integer.parseInt(redisTemplate.opsForValue().get(key)) + 1;
                redisTemplate.opsForValue().set(key, String.valueOf(getValue), limit.time(), TimeUnit.MILLISECONDS);
            }
            int count = Integer.parseInt(redisTemplate.opsForValue().get(key));
            if (count == 1) {
                //创建一个定时器
                TimerTask timerTask = new TimerTask() {
                    @Override
                    public void run() {
                        redisTemplate.delete(key);
                    }
                };
                //这个定时器设定在time规定的时间之后会执行上面的remove方法,也就是说在这个时间后它可以重新访问
                new Timer().schedule(timerTask, limit.time());
            }
            if (count > limit.count()) {
                logger.info("用户IP[" + ip + "]访问地址[" + url + "]超过了限定的次数[" + limit.count() + "]");
                // throw new RequestLimitException();
                //端口号
                String toLomitPath = "http://" + request.getServerName() + ":" + request.getServerPort() + limitPath;
                response.sendRedirect(toLomitPath);
            }
        } catch (RequestLimitException e) {
            throw e;
        } catch (Exception e) {
            logger.error("发生异常", e);
        }
    }


}

5.编写异常重定向类

@RestController
@RequestMapping("/safeLimit")
public class SafeLimitController {
    
    @RequestMapping("/limit")
    public MessageInfo limit(){
    	// 这里用自己的接口返回值就行
        return ResultGenerator.genFailResult("超出请求限制");
    }
    
}

6.接口上添加注解

@RequestLimit(count = 3, time = 10000)

count为次数,time为时间(毫秒)

  • 0
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值