JAVA Spring AOP+Annotation controller方法限制请求频率,限频

1 篇文章 0 订阅
1 篇文章 0 订阅

Spring AOP+Annotation实现请求限制频率

	使用Spring AOP+Annotation注解实现对Controller拦截,限制单ip对方法请求频率。

Annotation注解源代码

import java.lang.annotation.*;

/**
 * @author Chen
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented

public @interface RequestLimit {

    //多长时间内统计
    int time() default 10;

    //上限阈值
    int count() default 12;

    //触发上限后等待的时间
    int waits() default 10;

}

AOP源代码

import cn.cypcc.utils.RedisUtils;
import com.alibaba.fastjson.JSONObject;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
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.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;


/**
 * @author Chen
 */
@Aspect
@Component
@Order(1)
public class RequestLimitAspect {

    private static final String REQ_LIMIT = "REQ_LIMIT";
    private RedisUtils redisUtils;

    @Autowired
    public void setRedisUtils(RedisUtils redisUtils) {
        this.redisUtils = redisUtils;
    }

    /**
     * 定义拦截规则:拦截有@RequestLimit Annotation注解的方法
     * 。
     */
    @Around("@annotation(cn.cypcc.filter.RequestLimit)")
    public Object method(ProceedingJoinPoint pjp) throws Throwable {

        MethodSignature signature = (MethodSignature) pjp.getSignature();
        // 获取被拦截的方法
        Method method = signature.getMethod();
        RequestLimit limit = method.getAnnotation(RequestLimit.class);

        // No request for limit,continue processing request
        if (limit == null) {
            return pjp.proceed();
        }

        //获取当前线程的请求
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder
                .getRequestAttributes();
        HttpServletRequest request = requestAttributes.getRequest();

        int time = limit.time();
        int count = limit.count();
        int waits = limit.waits();

        //获取IP
        String ip = realIP(request);
        //获取请求的URL
        String url = request.getRequestURI();

        //生成KEY值
        String key = generateKey(url, ip);

		 /**
         * PS:  这里使用Redis存放用户请求次数
         *      如果请求量不大,可以使用JAVA数据结构如Map存放,但应设置为线程安全的
         */
        int nowCount = redisUtils.get(key) == null ? 0 : Integer.parseInt(redisUtils.get(key));

        if (nowCount == 0) {
            //初始访问该URL
            nowCount++;
            redisUtils.set(key, String.valueOf(nowCount), time, TimeUnit.SECONDS);
            return pjp.proceed();
        } else {
            //不是第一次访问
            redisUtils.increment(key);
            if (nowCount >= count) {
                //超出阈值
                //设置等待时间,并返回错误
                redisUtils.expire(key, waits, TimeUnit.SECONDS);
                return returnLimit();
            }
        }
        return pjp.proceed();
    }

    /**
     * @param url 请求的URL地址
     * @param ip  用户IP
     * @return KEY值
     */
    private static String generateKey(String url, String ip) {
        StringBuilder sb = new StringBuilder();
        sb.append(REQ_LIMIT);
        sb.append(url);
        sb.append("_");
        sb.append(ip);
        return sb.toString();
    }

    /**
     * 获取request的真实ip
     *
     * @param request 当前请求request
     * @return IP地址
     */
    private String realIP(HttpServletRequest request) {
        String xff = request.getHeader("x-forwarded-for");
        if (xff != null) {
            int index = xff.indexOf(',');
            if (index != -1) {
                xff = xff.substring(0, index);
            }
            return xff.trim();
        } else if (request.getHeader("x-real-ip") != null) {
            return request.getHeader("x-real-ip");
        }
        return request.getRemoteAddr();
    }

    /**
     * 返回拒绝信息
     *
     * @return 空
     * @throws IOException IOException
     */
    private String returnLimit() throws IOException {

        HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder
                .getRequestAttributes()).getResponse();
        PrintWriter out = response.getWriter();
        response.setCharacterEncoding("utf-8");
        response.setContentType("application/json; charset=utf-8");
        out.println(new JSONObject() {{
            put("code", "485");
            put("reason", "Access Denied");
        }}.toJSONString());
        out.flush();
        out.close();
        return null;

    }

	如果出现任何问题,欢迎留言,
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值