wang-hao-jie
2022-06-06 4e837c1e8c6f8a7252fb95776a1530ab737bb684
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
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 {
    }
}