1,pom引入依赖
com.github.kaligence requestLimit${requestLimit.version}
2,定义拦截器
import com.alibaba.fastjson.JSONObject;
import com.github.kaligence.requestLimit.RequestLimit;
import com.sun.java.browser.plugin2.liveconnect.v1.Result;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
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;
@Slf4j
@Component
public class RequestLimitIntercept extends HandlerInterceptorAdapter {
@Autowired
private RedisTemplate redisTemplate;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if(handler.getClass().isAssignableFrom(HandlerMethod.class)){
//HandlerMethod 封装方法定义相关的信息,如类,方法,参数等
HandlerMethod handlerMethod = (HandlerMethod) handler;
Method method = handlerMethod.getMethod();
// 获取方法中是否包含注解
RequestLimit methodAnnotation = method.getAnnotation(RequestLimit.class);
//获取 类中是否包含注解,也就是controller 是否有注解
RequestLimit classAnnotation = method.getDeclaringClass().getAnnotation(RequestLimit.class);
// 如果 方法上有注解就优先选择方法上的参数,否则类上的参数
RequestLimit requestLimit = methodAnnotation != null?methodAnnotation:classAnnotation;
if(requestLimit != null){
if(isLimit(request,requestLimit)){
Result result = new Result("訪問次數超過上線",false);
resonseOut(response, result);
return false;
}
}
}
return super.preHandle(request, response, handler);
}
//判断请求是否受限
public boolean isLimit(HttpServletRequest request,RequestLimit requestLimit){
// 受限的redis 缓存key ,用sessionId 来做唯一key,如果是app ,可以使用 用户ID 之类的唯一标识。
String limitKey = request.getServletPath()+request.getSession().getId();
// 从缓存中获取,当前这个请求访问了几次
Integer redisCount = (Integer) redisTemplate.opsForValue().get(limitKey);
log.info(limitKey + "在"+requestLimit.requestTime()+"S内被点击次数:"+redisCount);
if(redisCount == null){
//初始 次数
redisTemplate.opsForValue().set(limitKey,1,requestLimit.requestTime(), TimeUnit.SECONDS);
}else{
if(redisCount.intValue() >= requestLimit.requestCount()){
return true;
}
// 次数自增
redisTemplate.opsForValue().increment(limitKey,1);
//RedisUtils.incr(limitKey);
}
return false;
}
private void resonseOut(HttpServletResponse response, Result result) throws IOException {
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json; charset=utf-8");
PrintWriter out = null ;
String json = JSONObject.toJSON(result).toString();
out = response.getWriter();
out.append(json);
}
3,注册拦截器
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
@Configuration
public class InterceptorAdapterConfig extends WebMvcConfigurerAdapter {
@Autowired
private RequestLimitIntercept authorityInterceptor;
//private AuthorityInterceptor authorityInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry)
{
//注册自己的拦截器并设置拦截的请求路径
registry.addInterceptor(authorityInterceptor).addPathPatterns("/**");
super.addInterceptors(registry);
}
}
4,在controller中的方法上加入注解
@RequestLimit(requestCount = 2L,requestTime = 30L)
30秒内允许2个请求。



