package cn.exrick.xboot.core.config.interceptor;
|
|
import cn.exrick.xboot.core.common.annotation.RateLimiter;
|
import cn.exrick.xboot.core.common.constant.CommonConstant;
|
import cn.exrick.xboot.core.common.constant.SettingConstant;
|
import cn.exrick.xboot.core.common.exception.LimitException;
|
import cn.exrick.xboot.core.common.limit.RedisRaterLimiter;
|
import cn.exrick.xboot.core.common.utils.IpInfoUtil;
|
import cn.exrick.xboot.core.config.properties.XbootIpLimitProperties;
|
import cn.exrick.xboot.core.config.properties.XbootLimitProperties;
|
import cn.exrick.xboot.core.entity.Setting;
|
import cn.exrick.xboot.core.service.SettingService;
|
import cn.exrick.xboot.core.vo.OtherSetting;
|
import cn.hutool.core.util.StrUtil;
|
import com.google.gson.Gson;
|
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 Exrickx
|
*/
|
@Slf4j
|
@Component
|
public class LimitRaterInterceptor extends HandlerInterceptorAdapter {
|
|
@Autowired
|
private XbootLimitProperties limitProperties;
|
|
@Autowired
|
private XbootIpLimitProperties ipLimitProperties;
|
|
@Autowired
|
private RedisRaterLimiter redisRaterLimiter;
|
|
@Autowired
|
private IpInfoUtil ipInfoUtil;
|
|
@Autowired
|
private SettingService settingService;
|
|
public OtherSetting getOtherSetting() {
|
|
Setting setting = settingService.get(SettingConstant.OTHER_SETTING);
|
if (StrUtil.isBlank(setting.getValue())) {
|
return null;
|
}
|
return new Gson().fromJson(setting.getValue(), OtherSetting.class);
|
}
|
|
/**
|
* 预处理回调方法,实现处理器的预处理(如登录检查)
|
* 第三个参数为响应的处理器,即controller
|
* 返回true,表示继续流程,调用下一个拦截器或者处理器
|
* 返回false,表示流程中断,通过response产生响应
|
*/
|
@Override
|
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
|
Object handler) throws Exception {
|
|
String ip = ipInfoUtil.getIpAddr(request);
|
|
if (ipLimitProperties.getEnable()) {
|
Boolean token1 = redisRaterLimiter.acquireByRedis(ip,
|
ipLimitProperties.getLimit(), ipLimitProperties.getTimeout());
|
if (!token1) {
|
throw new LimitException("你手速怎么这么快,请点慢一点");
|
}
|
}
|
|
if (limitProperties.getEnable()) {
|
Boolean token2 = redisRaterLimiter.acquireByRedis(CommonConstant.LIMIT_ALL,
|
limitProperties.getLimit(), limitProperties.getTimeout());
|
if (!token2) {
|
throw new LimitException("当前访问总人数太多啦,请稍后再试");
|
}
|
}
|
|
// IP黑名单
|
OtherSetting os = getOtherSetting();
|
if (os != null && StrUtil.isNotBlank(os.getBlacklist())) {
|
String[] list = os.getBlacklist().split("\n");
|
for (String item : list) {
|
if (item.equals(ip)) {
|
throw new LimitException("您的IP已被添加至黑名单,请滚");
|
}
|
}
|
}
|
|
try {
|
HandlerMethod handlerMethod = (HandlerMethod) handler;
|
Object bean = handlerMethod.getBean();
|
Method method = handlerMethod.getMethod();
|
RateLimiter rateLimiter = method.getAnnotation(RateLimiter.class);
|
if (rateLimiter != null) {
|
String name = rateLimiter.name();
|
Long limit = rateLimiter.rate();
|
Long timeout = rateLimiter.rateInterval();
|
if(StrUtil.isBlank(name)){
|
name = StrUtil.subBefore(bean.toString(), "@", false) + "_" + method.getName();
|
}
|
if (rateLimiter.ipLimit()) {
|
name += "_" + ip;
|
}
|
Boolean token3 = redisRaterLimiter.acquireByRedis(name, limit, timeout);
|
if (!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 {
|
}
|
}
|