package cn.cetc54.platform.core.config.interceptor; import cn.cetc54.platform.core.common.annotation.RateLimiter; import cn.cetc54.platform.core.common.exception.LimitException; import cn.cetc54.platform.core.common.utils.IpInfoUtil; import cn.cetc54.platform.core.config.properties.PlatformLimitProperties; import cn.cetc54.platform.core.common.constant.CommonConstant; import cn.cetc54.platform.core.common.limit.RedisRaterLimiter; import cn.cetc54.platform.core.config.properties.PlatformIpLimitProperties; import cn.hutool.core.util.StrUtil; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.web.method.HandlerMethod; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.lang.reflect.Method; /** * 限流拦截器 * @author */ @Slf4j @Component public class LimitRaterInterceptor extends HandlerInterceptorAdapter { @Autowired private PlatformLimitProperties limitProperties; @Autowired private PlatformIpLimitProperties ipLimitProperties; @Autowired private RedisRaterLimiter redisRaterLimiter; @Autowired private IpInfoUtil ipInfoUtil; /** * 预处理回调方法,实现处理器的预处理(如登录检查) * 第三个参数为响应的处理器,即controller * 返回true,表示继续流程,调用下一个拦截器或者处理器 * 返回false,表示流程中断,通过response产生响应 */ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String ip = ipInfoUtil.getIpAddr(request); if(ipLimitProperties.getEnable()) { String token1 = redisRaterLimiter.acquireToken(ip, ipLimitProperties.getLimit(), ipLimitProperties.getTimeout()); if (StrUtil.isBlank(token1)) { throw new LimitException("你手速怎么这么快,请点慢一点"); } } if(limitProperties.getEnable()){ String token2 = redisRaterLimiter.acquireToken(CommonConstant.LIMIT_ALL, limitProperties.getLimit(), limitProperties.getTimeout()); if (StrUtil.isBlank(token2)) { throw new LimitException("当前访问总人数太多啦,请稍后再试"); } } try { HandlerMethod handlerMethod = (HandlerMethod) handler; Method method = handlerMethod.getMethod(); RateLimiter rateLimiter = method.getAnnotation(RateLimiter.class); if (rateLimiter != null) { Integer limit = rateLimiter.limit(); Long timeout = rateLimiter.timeout(); String token3 = redisRaterLimiter.acquireToken(method.getName(), limit, timeout); if (StrUtil.isBlank(token3)) { throw new LimitException("当前访问人数太多啦,请稍后再试"); } } }catch (LimitException e){ throw new LimitException(e.getMsg()); }catch (Exception e){ } return true; } /** * 当前请求进行处理之后,也就是Controller方法调用之后执行, * 但是它会在DispatcherServlet 进行视图返回渲染之前被调用。 * 此时我们可以通过modelAndView对模型数据进行处理或对视图进行处理。 */ @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { } /** * 方法将在整个请求结束之后,也就是在DispatcherServlet渲染了对应的视图之后执行。 * 这个方法的主要作用是用于进行资源清理工作的。 */ @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { } }