|
@@ -1,21 +1,25 @@
|
|
|
package com.qmth.boot.api.interceptor.impl;
|
|
|
|
|
|
+import com.qmth.boot.api.config.ApiConfig;
|
|
|
import com.qmth.boot.api.config.ApiConfigService;
|
|
|
import com.qmth.boot.api.config.ApiProperties;
|
|
|
+import com.qmth.boot.api.constant.ApiConstant;
|
|
|
import com.qmth.boot.api.exception.DefaultExceptionEnum;
|
|
|
import com.qmth.boot.api.interceptor.AbstractInterceptor;
|
|
|
import com.qmth.boot.api.utils.RequestUtil;
|
|
|
import com.qmth.boot.core.rateLimit.entity.RateLimitRule;
|
|
|
-import com.qmth.boot.core.rateLimit.service.RateLimitPolicy;
|
|
|
+import com.qmth.boot.core.rateLimit.entity.RateLimitTarget;
|
|
|
import com.qmth.boot.core.rateLimit.service.RateLimitService;
|
|
|
-import org.apache.commons.lang3.ArrayUtils;
|
|
|
+import com.qmth.boot.core.rateLimit.service.RateLimiter;
|
|
|
+import com.qmth.boot.core.security.model.AccessEntity;
|
|
|
import org.apache.commons.lang3.StringUtils;
|
|
|
-import org.springframework.beans.factory.annotation.Autowired;
|
|
|
import org.springframework.stereotype.Component;
|
|
|
|
|
|
import javax.annotation.Resource;
|
|
|
import javax.servlet.http.HttpServletRequest;
|
|
|
import javax.servlet.http.HttpServletResponse;
|
|
|
+import java.util.ArrayDeque;
|
|
|
+import java.util.Deque;
|
|
|
|
|
|
@Component
|
|
|
public class RateLimitInterceptor extends AbstractInterceptor {
|
|
@@ -29,26 +33,48 @@ public class RateLimitInterceptor extends AbstractInterceptor {
|
|
|
@Resource
|
|
|
private RateLimitService rateLimitService;
|
|
|
|
|
|
- @Autowired(required = false)
|
|
|
- private RateLimitPolicy rateLimitPolicy;
|
|
|
-
|
|
|
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
|
|
|
- String path = request.getServletPath();
|
|
|
- RateLimitRule[] rules = rateLimitPolicy != null ? rateLimitPolicy.getRules(path) : null;
|
|
|
- if (ArrayUtils.isEmpty(rules)) {
|
|
|
- rules = getAacConfig(apiProperties, apiConfigService, request, handler).getRateLimit();
|
|
|
- }
|
|
|
+ ApiConfig ac = getApiConfig(apiProperties, apiConfigService, request, handler);
|
|
|
+ String endpoint = request.getServletPath();
|
|
|
String device = getDeviceIdOrIpAddress(request);
|
|
|
+ String user = getUserIdentity(request);
|
|
|
// 所有限流规则需要全部匹配才通过
|
|
|
- for (RateLimitRule rule : rules) {
|
|
|
- if (!rateLimitService.accept(path, device, rule)) {
|
|
|
- log.warn("Rate limited: path={}, rule={}, device={}", path, rule.toString(), device);
|
|
|
+ // 已通过限流堆栈
|
|
|
+ Deque<RateLimiter> stack = new ArrayDeque<>();
|
|
|
+ for (RateLimitRule rule : ac.getRateLimit()) {
|
|
|
+ String target = null;
|
|
|
+ if (rule.getTarget() == RateLimitTarget.DEVICE) {
|
|
|
+ target = device;
|
|
|
+ } else if (rule.getTarget() == RateLimitTarget.USER) {
|
|
|
+ target = user;
|
|
|
+ }
|
|
|
+ RateLimiter limiter = rateLimitService.getRateLimiter(endpoint, target, rule);
|
|
|
+ if (limiter.acquire()) {
|
|
|
+ stack.push(limiter);
|
|
|
+ } else {
|
|
|
+ while (!stack.isEmpty()) {
|
|
|
+ stack.pop().release();
|
|
|
+ }
|
|
|
+ log.warn("Rate limited: endpoint={}, rule={}, target={}", endpoint, rule.toString(), target);
|
|
|
throw DefaultExceptionEnum.RATE_LIMITED.exception();
|
|
|
}
|
|
|
}
|
|
|
+ request.setAttribute(ATTRIBUTE_RATE_LIMITER_STACK, stack);
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
+ @Override
|
|
|
+ public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
|
|
|
+ Exception ex) {
|
|
|
+ Deque<RateLimiter> stack = RequestUtil.getAttribute(request, ApiConstant.ATTRIBUTE_RATE_LIMITER_STACK);
|
|
|
+ if (stack != null) {
|
|
|
+ log.debug("Rate limit stack size: " + stack.size());
|
|
|
+ while (!stack.isEmpty()) {
|
|
|
+ stack.pop().release();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
private String getDeviceIdOrIpAddress(HttpServletRequest request) {
|
|
|
// deviceId不存在时取IP地址作为设备标识
|
|
|
String device = StringUtils.trimToNull(request.getHeader(HEADER_DEVICE_ID));
|
|
@@ -58,4 +84,13 @@ public class RateLimitInterceptor extends AbstractInterceptor {
|
|
|
return device;
|
|
|
}
|
|
|
|
|
|
+ private String getUserIdentity(HttpServletRequest request) {
|
|
|
+ AccessEntity accessEntity = RequestUtil.getAttribute(request, ApiConstant.ATTRIBUTE_ACCESS_ENTITY);
|
|
|
+ if (accessEntity != null) {
|
|
|
+ return accessEntity.getIdentity();
|
|
|
+ } else {
|
|
|
+ return StringUtils.EMPTY;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
}
|