mirror of
https://gitee.com/dromara/RuoYi-Vue-Plus.git
synced 2026-09-20 02:08:15 +08:00
POST请求不执行问题处理:原因contentType 不对, body为空
Header 头部AK移除: 直接移除当前request中的就行,不需要赋值
This commit is contained in:
parent
d543236366
commit
373c9af867
@ -78,7 +78,7 @@ spring:
|
|||||||
# 端口,默认为6379
|
# 端口,默认为6379
|
||||||
port: 6379
|
port: 6379
|
||||||
# 数据库索引
|
# 数据库索引
|
||||||
database: 0
|
database: 11
|
||||||
# 密码
|
# 密码
|
||||||
password:
|
password:
|
||||||
# 连接超时时间
|
# 连接超时时间
|
||||||
|
|||||||
@ -0,0 +1,14 @@
|
|||||||
|
package com.ruoyi.gateway.exception;
|
||||||
|
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 网关异常-AK必要
|
||||||
|
*
|
||||||
|
* @author Wenchao Gong
|
||||||
|
* @date 2021-10-04
|
||||||
|
*/
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class AkRequireException extends GatewayException {
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
package com.ruoyi.gateway.exception;
|
||||||
|
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 网关异常-请求方法不支持
|
||||||
|
*
|
||||||
|
* @author Wenchao Gong
|
||||||
|
* @date 2021-10-04
|
||||||
|
*/
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class ContentTypeNotSupportedException extends GatewayException {
|
||||||
|
|
||||||
|
}
|
||||||
@ -1,11 +1,14 @@
|
|||||||
package com.ruoyi.gateway.exception;
|
package com.ruoyi.gateway.exception;
|
||||||
|
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* 网关异常
|
||||||
|
*
|
||||||
* @author Wenchao Gong
|
* @author Wenchao Gong
|
||||||
* @date 2021-10-04
|
* @date 2021-10-04
|
||||||
*/
|
*/
|
||||||
|
@NoArgsConstructor
|
||||||
public class GatewayException extends RuntimeException {
|
public class GatewayException extends RuntimeException {
|
||||||
public GatewayException(String msg) {
|
|
||||||
super(msg);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,14 @@
|
|||||||
|
package com.ruoyi.gateway.exception;
|
||||||
|
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 网关异常-请求方法不支持
|
||||||
|
*
|
||||||
|
* @author Wenchao Gong
|
||||||
|
* @date 2021-10-04
|
||||||
|
*/
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class MethodNotSupportedException extends GatewayException {
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,28 @@
|
|||||||
|
package com.ruoyi.gateway.exception;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 网关异常-限流异常
|
||||||
|
*
|
||||||
|
* @author Wenchao Gong
|
||||||
|
* @date 2021-10-04
|
||||||
|
*/
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class RateLimitException extends GatewayException {
|
||||||
|
|
||||||
|
private TimeUnit timeUnit;
|
||||||
|
private int replenishRate;
|
||||||
|
private int tokensLeft;
|
||||||
|
private String message;
|
||||||
|
public RateLimitException(int replenishRate, int tokensLeft, TimeUnit timeUnit) {
|
||||||
|
this.replenishRate = replenishRate;
|
||||||
|
this.tokensLeft = tokensLeft;
|
||||||
|
this.timeUnit = timeUnit;
|
||||||
|
String unit = StrUtil.upperFirst(timeUnit.name().toLowerCase());
|
||||||
|
message = String.format("服务限流: 上限[%d/%s], 实际[%d/%s]", replenishRate, unit, tokensLeft, unit);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
package com.ruoyi.gateway.exception;
|
||||||
|
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 网关异常-规则已过期
|
||||||
|
*
|
||||||
|
* @author Wenchao Gong
|
||||||
|
* @date 2021-10-04
|
||||||
|
*/
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class RuleExpiredException extends GatewayException {
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
package com.ruoyi.gateway.exception;
|
||||||
|
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 网关异常-规则不存在
|
||||||
|
*
|
||||||
|
* @author Wenchao Gong
|
||||||
|
* @date 2021-10-04
|
||||||
|
*/
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class RuleNotExistException extends GatewayException {
|
||||||
|
|
||||||
|
}
|
||||||
@ -1,9 +1,11 @@
|
|||||||
package com.ruoyi.gateway.filter;
|
package com.ruoyi.gateway.filter;
|
||||||
|
|
||||||
|
import cn.hutool.core.lang.Assert;
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
import cn.hutool.json.JSONArray;
|
import cn.hutool.json.JSONArray;
|
||||||
import cn.hutool.json.JSONObject;
|
import cn.hutool.json.JSONObject;
|
||||||
import cn.hutool.json.JSONUtil;
|
import cn.hutool.json.JSONUtil;
|
||||||
|
import com.ruoyi.gateway.exception.*;
|
||||||
import com.ruoyi.gateway.ratelimit.CustomerRedisRateLimiter;
|
import com.ruoyi.gateway.ratelimit.CustomerRedisRateLimiter;
|
||||||
import com.ruoyi.gateway.utils.GatewayUtils;
|
import com.ruoyi.gateway.utils.GatewayUtils;
|
||||||
import com.ruoyi.gateway.utils.beans.IscRule;
|
import com.ruoyi.gateway.utils.beans.IscRule;
|
||||||
@ -12,13 +14,13 @@ import org.springframework.cloud.gateway.filter.GlobalFilter;
|
|||||||
import org.springframework.cloud.gateway.route.Route;
|
import org.springframework.cloud.gateway.route.Route;
|
||||||
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
|
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
|
||||||
import org.springframework.core.Ordered;
|
import org.springframework.core.Ordered;
|
||||||
|
import org.springframework.http.HttpHeaders;
|
||||||
import org.springframework.http.HttpMethod;
|
import org.springframework.http.HttpMethod;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||||
import org.springframework.util.LinkedMultiValueMap;
|
import org.springframework.util.LinkedMultiValueMap;
|
||||||
import org.springframework.util.MultiValueMap;
|
import org.springframework.util.MultiValueMap;
|
||||||
import org.springframework.util.StringUtils;
|
|
||||||
import org.springframework.web.reactive.function.server.HandlerStrategies;
|
import org.springframework.web.reactive.function.server.HandlerStrategies;
|
||||||
import org.springframework.web.reactive.function.server.ServerRequest;
|
import org.springframework.web.reactive.function.server.ServerRequest;
|
||||||
import org.springframework.web.server.ServerWebExchange;
|
import org.springframework.web.server.ServerWebExchange;
|
||||||
@ -31,7 +33,6 @@ import java.util.concurrent.TimeUnit;
|
|||||||
import java.util.function.BiConsumer;
|
import java.util.function.BiConsumer;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
|
||||||
|
|
||||||
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.setResponseStatus;
|
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.setResponseStatus;
|
||||||
import static org.springframework.util.CollectionUtils.unmodifiableMultiValueMap;
|
import static org.springframework.util.CollectionUtils.unmodifiableMultiValueMap;
|
||||||
@ -49,10 +50,12 @@ import static org.springframework.util.CollectionUtils.unmodifiableMultiValueMap
|
|||||||
*/
|
*/
|
||||||
public class CustomerGlobalFilter implements GlobalFilter, Ordered {
|
public class CustomerGlobalFilter implements GlobalFilter, Ordered {
|
||||||
private final CustomerRedisRateLimiter rateLimiter;
|
private final CustomerRedisRateLimiter rateLimiter;
|
||||||
|
|
||||||
public CustomerGlobalFilter(CustomerRedisRateLimiter rateLimiter) {
|
public CustomerGlobalFilter(CustomerRedisRateLimiter rateLimiter) {
|
||||||
this.rateLimiter = rateLimiter;
|
this.rateLimiter = rateLimiter;
|
||||||
}
|
}
|
||||||
public static final TimeUnit[] TIME_UNITS = {TimeUnit.SECONDS, TimeUnit.MINUTES, TimeUnit.HOURS, TimeUnit.DAYS};
|
|
||||||
|
private static final TimeUnit[] TIME_UNITS = {TimeUnit.SECONDS, TimeUnit.MINUTES, TimeUnit.HOURS, TimeUnit.DAYS};
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
|
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
|
||||||
@ -64,75 +67,144 @@ public class CustomerGlobalFilter implements GlobalFilter, Ordered {
|
|||||||
final String accessKeyName = String.valueOf(metadata.get(GatewayUtils.CONFIG_ACCESS_KEY_NAME_KEY));
|
final String accessKeyName = String.valueOf(metadata.get(GatewayUtils.CONFIG_ACCESS_KEY_NAME_KEY));
|
||||||
String headerAk = GatewayUtils.getValue(null, () -> request.getHeaders().get(accessKeyName));
|
String headerAk = GatewayUtils.getValue(null, () -> request.getHeaders().get(accessKeyName));
|
||||||
if (HttpMethod.GET.equals(httpMethod)) {
|
if (HttpMethod.GET.equals(httpMethod)) {
|
||||||
final MultiValueMap<String, String> queryParams = new LinkedMultiValueMap<>(request.getQueryParams());
|
return handleGetRequest(exchange, chain, route, request, accessKeyName, headerAk);
|
||||||
final IscRule rule = handleRule(headerAk, () -> queryParams.get(accessKeyName), route);
|
|
||||||
Supplier<Mono<Void>> rateLimiterAfterSupplier = () -> {
|
|
||||||
removeParam(headerAk, request, accessKeyName, () -> queryParams.remove(accessKeyName));
|
|
||||||
handleHiddenParams(route, queryParams, (next, map) -> {
|
|
||||||
Object value;
|
|
||||||
if (Objects.isNull(value = next.getValue())) {
|
|
||||||
map.add(next.getKey(), StrUtil.EMPTY);
|
|
||||||
} else if (value instanceof JSONArray) {
|
|
||||||
map.put(next.getKey(), ((JSONArray) value).toList(String.class));
|
|
||||||
} else {
|
|
||||||
map.add(next.getKey(), value.toString());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
URI newUri = UriComponentsBuilder.fromUri(request.getURI())
|
|
||||||
.replaceQueryParams(unmodifiableMultiValueMap(queryParams)).build().toUri();
|
|
||||||
ServerHttpRequest updatedRequest = exchange.getRequest().mutate().uri(newUri).build();
|
|
||||||
return chain.filter(exchange.mutate().request(updatedRequest).build());
|
|
||||||
};
|
|
||||||
|
|
||||||
return rateLimiter(exchange, rule, route, 0, rateLimiterAfterSupplier);
|
|
||||||
} else if (HttpMethod.POST.equals(httpMethod)) {
|
} else if (HttpMethod.POST.equals(httpMethod)) {
|
||||||
final ServerRequest serverRequest = ServerRequest.create(exchange, HandlerStrategies.withDefaults().messageReaders());
|
return handlePostRequest(exchange, chain, route, request, accessKeyName, headerAk);
|
||||||
final Mono<String> modifiedBody = serverRequest.bodyToMono(String.class).flatMap(body -> {
|
}
|
||||||
|
throw new MethodNotSupportedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理POST 请求
|
||||||
|
*
|
||||||
|
* @param exchange 当前服务交换器
|
||||||
|
* @param chain 当前过滤链
|
||||||
|
* @param route 路由信息
|
||||||
|
* @param request 请求信息
|
||||||
|
* @param accessKeyName AK键名
|
||||||
|
* @param headerAk 头部AK信息
|
||||||
|
* @return 指示请求处理何时完成
|
||||||
|
*/
|
||||||
|
private Mono<Void> handlePostRequest(ServerWebExchange exchange, GatewayFilterChain chain, Route route,
|
||||||
|
ServerHttpRequest request, String accessKeyName, String headerAk) {
|
||||||
|
final ServerRequest serverRequest = ServerRequest.create(exchange,
|
||||||
|
HandlerStrategies.withDefaults().messageReaders());
|
||||||
|
final Mono<String> modifiedBody = serverRequest.bodyToMono(String.class).defaultIfEmpty(StrUtil.EMPTY)
|
||||||
|
.flatMap(body -> {
|
||||||
MediaType mediaType = request.getHeaders().getContentType();
|
MediaType mediaType = request.getHeaders().getContentType();
|
||||||
if (MediaType.APPLICATION_JSON.equals(mediaType)) {
|
if (MediaType.APPLICATION_JSON.equals(mediaType)) {
|
||||||
JSONObject jsonObj = JSONUtil.parseObj(body);
|
return handlePostRequestJson(exchange, route, request, accessKeyName, headerAk, body);
|
||||||
final IscRule rule = handleRule(headerAk, () ->
|
|
||||||
Arrays.asList(jsonObj.get(accessKeyName, String.class, true)), route);
|
|
||||||
Supplier<Mono<String>> rateLimiterAfterSupplier = () -> {
|
|
||||||
removeParam(headerAk, request, accessKeyName, () -> jsonObj.remove(accessKeyName));
|
|
||||||
handleHiddenParams(route, jsonObj, (next, map) -> {
|
|
||||||
map.set(next.getKey(), next.getValue());
|
|
||||||
});
|
|
||||||
return Mono.just(jsonObj.toString());
|
|
||||||
};
|
|
||||||
|
|
||||||
return rateLimiter(exchange, rule, route, 0, rateLimiterAfterSupplier);
|
|
||||||
} else if (MediaType.APPLICATION_FORM_URLENCODED.equals(mediaType)) {
|
} else if (MediaType.APPLICATION_FORM_URLENCODED.equals(mediaType)) {
|
||||||
if (StringUtils.hasText(body)) {
|
return handlePostRequestFormUrlencoded(exchange, route, request, accessKeyName, headerAk, body);
|
||||||
final Stream<String[]> stream = Arrays.stream(body.split("&")).map(param -> param.split("="));
|
|
||||||
final IscRule rule = handleRule(headerAk, () -> stream.filter(param -> param.length > 0 &&
|
|
||||||
accessKeyName.equals(param[0])).map(param -> param[1]).collect(Collectors.toList()), route);
|
|
||||||
Supplier<Mono<String>> rateLimiterAfterSupplier = () -> {
|
|
||||||
removeParam(headerAk, request, accessKeyName, null);
|
|
||||||
final List<String[]> params = stream.filter(param -> !accessKeyName.equals(param[0])).collect(Collectors.toList());
|
|
||||||
handleHiddenParams(route, params, (next, list) -> {
|
|
||||||
Object value;
|
|
||||||
if (Objects.isNull(value = next.getValue())) {
|
|
||||||
list.add(new String[]{next.getKey(), StrUtil.EMPTY});
|
|
||||||
} else if (value instanceof JSONArray) {
|
|
||||||
((JSONArray) value).stream().map(o -> String.valueOf(o)).forEach(v -> {
|
|
||||||
list.add(new String[]{next.getKey(), v});
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
list.add(new String[]{next.getKey(), next.getValue().toString()});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return Mono.just(params.stream().map(param -> param[0] + '=' + param[1]).collect(Collectors.joining("&")));
|
|
||||||
};
|
|
||||||
|
|
||||||
return rateLimiter(exchange, rule, route, 0, rateLimiterAfterSupplier);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return Mono.empty();
|
throw new ContentTypeNotSupportedException();
|
||||||
});
|
});
|
||||||
return GatewayUtils.modifyBody(exchange, chain, modifiedBody);
|
return GatewayUtils.modifyBody(exchange, chain, modifiedBody);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理POST APPLICATION_FORM_URLENCODED 请求
|
||||||
|
*
|
||||||
|
* @param exchange 当前服务交换器
|
||||||
|
* @param route 路由信息
|
||||||
|
* @param request 请求信息
|
||||||
|
* @param accessKeyName AK键名
|
||||||
|
* @param headerAk 头部AK信息
|
||||||
|
* @param body 请求Body信息
|
||||||
|
* @return 处理后的Body信息
|
||||||
|
*/
|
||||||
|
private Mono<? extends String> handlePostRequestFormUrlencoded(ServerWebExchange exchange, Route route,
|
||||||
|
ServerHttpRequest request, String accessKeyName,
|
||||||
|
String headerAk, String body) {
|
||||||
|
|
||||||
|
if (StrUtil.isBlank(body)) {
|
||||||
|
Assert.notBlank(headerAk, AkRequireException::new);
|
||||||
}
|
}
|
||||||
return chain.filter(exchange);
|
final List<String[]> srcParams = Arrays.stream(body.split("&")).map(param -> param.split("="))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
final IscRule rule = handleRule(headerAk, () -> srcParams.stream()
|
||||||
|
.filter(param -> param.length > 0 && accessKeyName.equals(param[0]))
|
||||||
|
.map(param -> param[1]).collect(Collectors.toList()), route);
|
||||||
|
Supplier<Mono<String>> rateLimiterAfterSupplier = () -> {
|
||||||
|
removeParam(headerAk, request, accessKeyName, null);
|
||||||
|
final List<String[]> params = srcParams.stream().filter(param -> !accessKeyName.equals(param[0]))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
handleHiddenParams(route, params, (next, list) -> {
|
||||||
|
Object value;
|
||||||
|
if (Objects.isNull(value = next.getValue())) {
|
||||||
|
list.add(new String[]{next.getKey(), StrUtil.EMPTY});
|
||||||
|
} else if (value instanceof JSONArray) {
|
||||||
|
((JSONArray) value).stream().map(String::valueOf)
|
||||||
|
.forEach(v -> list.add(new String[]{next.getKey(), v}));
|
||||||
|
} else {
|
||||||
|
list.add(new String[]{next.getKey(), next.getValue().toString()});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return Mono.just(params.stream().map(param -> param[0] + '=' + param[1]).collect(Collectors.joining("&")));
|
||||||
|
};
|
||||||
|
|
||||||
|
return rateLimiter(exchange, rule, route, 0, rateLimiterAfterSupplier);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理POST APPLICATION_JSON 请求
|
||||||
|
*
|
||||||
|
* @param exchange 当前服务交换器
|
||||||
|
* @param route 路由信息
|
||||||
|
* @param request 请求信息
|
||||||
|
* @param accessKeyName AK键名
|
||||||
|
* @param headerAk 头部AK信息
|
||||||
|
* @param body 请求Body信息
|
||||||
|
* @return 处理后的Body信息
|
||||||
|
*/
|
||||||
|
private Mono<? extends String> handlePostRequestJson(ServerWebExchange exchange, Route route,
|
||||||
|
ServerHttpRequest request, String accessKeyName,
|
||||||
|
String headerAk, String body) {
|
||||||
|
JSONObject jsonObj = StrUtil.isBlank(body) ? new JSONObject() : JSONUtil.parseObj(body);
|
||||||
|
final IscRule rule = handleRule(headerAk, () -> Collections.singletonList(jsonObj.get(accessKeyName,
|
||||||
|
String.class, true)), route);
|
||||||
|
Supplier<Mono<String>> rateLimiterAfterSupplier = () -> {
|
||||||
|
removeParam(headerAk, request, accessKeyName, () -> jsonObj.remove(accessKeyName));
|
||||||
|
handleHiddenParams(route, jsonObj, (next, map) -> map.set(next.getKey(), next.getValue()));
|
||||||
|
return Mono.just(jsonObj.toString());
|
||||||
|
};
|
||||||
|
|
||||||
|
return rateLimiter(exchange, rule, route, 0, rateLimiterAfterSupplier);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理GET 请求
|
||||||
|
*
|
||||||
|
* @param exchange 当前服务交换器
|
||||||
|
* @param chain 当前过滤链
|
||||||
|
* @param route 路由信息
|
||||||
|
* @param request 请求信息
|
||||||
|
* @param accessKeyName AK键名
|
||||||
|
* @param headerAk 头部AK信息
|
||||||
|
* @return 指示请求处理何时完成
|
||||||
|
*/
|
||||||
|
private Mono<Void> handleGetRequest(ServerWebExchange exchange, GatewayFilterChain chain, Route route,
|
||||||
|
ServerHttpRequest request, String accessKeyName, String headerAk) {
|
||||||
|
final MultiValueMap<String, String> queryParams = new LinkedMultiValueMap<>(request.getQueryParams());
|
||||||
|
final IscRule rule = handleRule(headerAk, () -> queryParams.get(accessKeyName), route);
|
||||||
|
Supplier<Mono<Void>> rateLimiterAfterSupplier = () -> {
|
||||||
|
removeParam(headerAk, request, accessKeyName, () -> queryParams.remove(accessKeyName));
|
||||||
|
handleHiddenParams(route, queryParams, (next, map) -> {
|
||||||
|
Object value;
|
||||||
|
if (Objects.isNull(value = next.getValue())) {
|
||||||
|
map.add(next.getKey(), StrUtil.EMPTY);
|
||||||
|
} else if (value instanceof JSONArray) {
|
||||||
|
map.put(next.getKey(), ((JSONArray) value).toList(String.class));
|
||||||
|
} else {
|
||||||
|
map.add(next.getKey(), value.toString());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
URI newUri = UriComponentsBuilder.fromUri(request.getURI())
|
||||||
|
.replaceQueryParams(unmodifiableMultiValueMap(queryParams)).build().toUri();
|
||||||
|
ServerHttpRequest updatedRequest = exchange.getRequest().mutate().uri(newUri).build();
|
||||||
|
return chain.filter(exchange.mutate().request(updatedRequest).build());
|
||||||
|
};
|
||||||
|
return rateLimiter(exchange, rule, route, 0, rateLimiterAfterSupplier);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -143,19 +215,19 @@ public class CustomerGlobalFilter implements GlobalFilter, Ordered {
|
|||||||
/**
|
/**
|
||||||
* 处理规则
|
* 处理规则
|
||||||
*
|
*
|
||||||
* @param headerAk
|
* @param headerAk 头部AK
|
||||||
* @param valueSupplier
|
* @param valueSupplier valueList 生产者
|
||||||
* @param route
|
* @param route 路由信息
|
||||||
* @return
|
* @return AK对应服务规则信息
|
||||||
*/
|
*/
|
||||||
private IscRule handleRule(String headerAk, Supplier<List<String>> valueSupplier, Route route) {
|
private IscRule handleRule(String headerAk, Supplier<List<String>> valueSupplier, Route route) {
|
||||||
//获取AK
|
//获取AK
|
||||||
final String ak = GatewayUtils.getValue(headerAk, valueSupplier, () -> new RuntimeException("AK 不存在"));
|
final String ak = GatewayUtils.getValue(headerAk, valueSupplier, AkRequireException::new);
|
||||||
//获取规则
|
//获取规则
|
||||||
final IscRule rule = GatewayUtils.getRequiredValue(() -> GatewayUtils.getRule(ak, route.getId()),
|
final IscRule rule = GatewayUtils.getRequiredValue(() -> GatewayUtils.getRule(ak, route.getId()),
|
||||||
() -> new RuntimeException("AK异常"));
|
RuleNotExistException::new);
|
||||||
//是否到期
|
//是否到期
|
||||||
GatewayUtils.isBefore(rule, () -> new RuntimeException("AK已过期"));
|
GatewayUtils.isBefore(rule, RuleExpiredException::new);
|
||||||
//设置AK 到ID 为了传参方便
|
//设置AK 到ID 为了传参方便
|
||||||
rule.setId(ak);
|
rule.setId(ak);
|
||||||
return rule;
|
return rule;
|
||||||
@ -163,18 +235,23 @@ public class CustomerGlobalFilter implements GlobalFilter, Ordered {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 删除参数
|
* 删除参数
|
||||||
* @param headerAk
|
*
|
||||||
* @param request
|
* @param headerAk 头部AK
|
||||||
* @param accessKeyName
|
* @param request 请求
|
||||||
* @param removeSupplier
|
* @param accessKeyName AK名称
|
||||||
* @param <T>
|
* @param removeSupplier 删除参数提供者
|
||||||
|
* @param <T> 类型
|
||||||
*/
|
*/
|
||||||
private <T> void removeParam(String headerAk, ServerHttpRequest request, String accessKeyName, Supplier<T> removeSupplier) {
|
private <T> void removeParam(String headerAk, ServerHttpRequest request, String accessKeyName,
|
||||||
|
Supplier<T> removeSupplier) {
|
||||||
//如果header中有AK,则删除
|
//如果header中有AK,则删除
|
||||||
if (Objects.nonNull(headerAk)) {
|
if (Objects.nonNull(headerAk)) {
|
||||||
request.getHeaders().remove(accessKeyName);
|
final HttpHeaders headers = request.getHeaders();
|
||||||
|
if(headers.containsKey(accessKeyName)) {
|
||||||
|
request.mutate().headers(headMap -> headMap.remove(accessKeyName)).build();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if(Objects.nonNull(removeSupplier)) {
|
if (Objects.nonNull(removeSupplier)) {
|
||||||
removeSupplier.get();
|
removeSupplier.get();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -182,10 +259,10 @@ public class CustomerGlobalFilter implements GlobalFilter, Ordered {
|
|||||||
/**
|
/**
|
||||||
* 处理隐藏参数
|
* 处理隐藏参数
|
||||||
*
|
*
|
||||||
* @param route
|
* @param route 路由信息
|
||||||
* @param result
|
* @param result 参数
|
||||||
* @param mapper
|
* @param mapper 处理参数方法
|
||||||
* @param <U>
|
* @param <U> 参数类型
|
||||||
*/
|
*/
|
||||||
private <U> void handleHiddenParams(Route route, U result, BiConsumer<Map.Entry<String, Object>, U> mapper) {
|
private <U> void handleHiddenParams(Route route, U result, BiConsumer<Map.Entry<String, Object>, U> mapper) {
|
||||||
final Object obj = route.getMetadata().get(GatewayUtils.CONFIG_ADD_PARAM_KEY);
|
final Object obj = route.getMetadata().get(GatewayUtils.CONFIG_ADD_PARAM_KEY);
|
||||||
@ -196,29 +273,29 @@ public class CustomerGlobalFilter implements GlobalFilter, Ordered {
|
|||||||
if (json.isEmpty()) {
|
if (json.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Iterator<Map.Entry<String, Object>> iterator = json.entrySet().iterator();
|
for (Map.Entry<String, Object> next : json.entrySet()) {
|
||||||
while (iterator.hasNext()) {
|
|
||||||
Map.Entry<String, Object> next = iterator.next();
|
|
||||||
mapper.accept(next, result);
|
mapper.accept(next, result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 限流
|
* 限流
|
||||||
* @param exchange
|
*
|
||||||
* @param rule
|
* @param exchange 当前服务交换器
|
||||||
* @param route
|
* @param rule 限流规则
|
||||||
|
* @param route 路由
|
||||||
* @param rateLimiterAfterSupplier 限流后操作(删除参数、添加隐藏参数,跳转)
|
* @param rateLimiterAfterSupplier 限流后操作(删除参数、添加隐藏参数,跳转)
|
||||||
* @param <T>
|
* @param <T> 最终返回类型
|
||||||
* @return
|
* @return rateLimiterAfterSupplier 返回类型
|
||||||
*/
|
*/
|
||||||
private <T extends Object> Mono rateLimiter(ServerWebExchange exchange, IscRule rule, Route route, final int index,
|
private <T> Mono<T> rateLimiter(ServerWebExchange exchange, IscRule rule, Route route, final int index,
|
||||||
Supplier<Mono<T>> rateLimiterAfterSupplier) {
|
Supplier<Mono<T>> rateLimiterAfterSupplier) {
|
||||||
final TimeUnit timeUnit = TIME_UNITS[index];
|
final TimeUnit timeUnit = TIME_UNITS[index];
|
||||||
final Long limit = TimeUnit.SECONDS.equals(timeUnit) ? rule.getSecondsLimit() : TimeUnit.MINUTES.equals(timeUnit)
|
final Long limit = TimeUnit.SECONDS.equals(timeUnit) ? rule.getSecondsLimit() :
|
||||||
? rule.getMinutesLimit() : TimeUnit.HOURS.equals(timeUnit) ? rule.getHoursLimit() : rule.getDaysLimit();
|
TimeUnit.MINUTES.equals(timeUnit)
|
||||||
if(Objects.isNull(limit) || limit <= 0L) {
|
? rule.getMinutesLimit() : TimeUnit.HOURS.equals(timeUnit) ? rule.getHoursLimit() : rule.getDaysLimit();
|
||||||
if(TimeUnit.DAYS.equals(timeUnit)) {
|
if (Objects.isNull(limit) || limit <= 0L) {
|
||||||
|
if (TimeUnit.DAYS.equals(timeUnit)) {
|
||||||
return rateLimiterAfterSupplier.get();
|
return rateLimiterAfterSupplier.get();
|
||||||
}
|
}
|
||||||
return rateLimiter(exchange, rule, route, index + 1, rateLimiterAfterSupplier);
|
return rateLimiter(exchange, rule, route, index + 1, rateLimiterAfterSupplier);
|
||||||
@ -228,13 +305,14 @@ public class CustomerGlobalFilter implements GlobalFilter, Ordered {
|
|||||||
exchange.getResponse().getHeaders().add(header.getKey(), header.getValue());
|
exchange.getResponse().getHeaders().add(header.getKey(), header.getValue());
|
||||||
}
|
}
|
||||||
if (response.isAllowed()) {
|
if (response.isAllowed()) {
|
||||||
if(TimeUnit.DAYS.equals(timeUnit)) {
|
if (TimeUnit.DAYS.equals(timeUnit)) {
|
||||||
return rateLimiterAfterSupplier.get();
|
return rateLimiterAfterSupplier.get();
|
||||||
}
|
}
|
||||||
return rateLimiter(exchange, rule, route, index + 1, rateLimiterAfterSupplier);
|
return rateLimiter(exchange, rule, route, index + 1, rateLimiterAfterSupplier);
|
||||||
}
|
}
|
||||||
setResponseStatus(exchange, HttpStatus.TOO_MANY_REQUESTS);
|
setResponseStatus(exchange, HttpStatus.TOO_MANY_REQUESTS);
|
||||||
return exchange.getResponse().setComplete();
|
exchange.getResponse().setComplete();
|
||||||
|
throw new RateLimitException(limit.intValue(), 1, timeUnit);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -44,41 +44,46 @@ public class GatewayUtils {
|
|||||||
public static final Codec RULE_CODES_INSTANCE = new TypedJsonJacksonCodec(String.class, IscRule.class);
|
public static final Codec RULE_CODES_INSTANCE = new TypedJsonJacksonCodec(String.class, IscRule.class);
|
||||||
private static RedissonClient client = SpringUtil.getBean(RedissonClient.class);
|
private static RedissonClient client = SpringUtil.getBean(RedissonClient.class);
|
||||||
|
|
||||||
public static Mono<Void> modifyBody(ServerWebExchange exchange, GatewayFilterChain chain, Mono<String> publisher)
|
/**
|
||||||
{
|
* 修改请求Body
|
||||||
|
*
|
||||||
|
* @param exchange 当前服务交换器
|
||||||
|
* @param chain 当前过滤链
|
||||||
|
* @param publisher body体提供者
|
||||||
|
* @return 指示请求处理何时完成
|
||||||
|
*/
|
||||||
|
public static Mono<Void> modifyBody(ServerWebExchange exchange, GatewayFilterChain chain, Mono<String> publisher) {
|
||||||
BodyInserter bodyInserter = BodyInserters.fromPublisher(publisher, String.class);
|
BodyInserter bodyInserter = BodyInserters.fromPublisher(publisher, String.class);
|
||||||
HttpHeaders headers = new HttpHeaders();
|
HttpHeaders headers = new HttpHeaders();
|
||||||
headers.putAll(exchange.getRequest().getHeaders());
|
headers.putAll(exchange.getRequest().getHeaders());
|
||||||
headers.remove(HttpHeaders.CONTENT_LENGTH);
|
headers.remove(HttpHeaders.CONTENT_LENGTH);
|
||||||
CachedBodyOutputMessage outputMessage = new CachedBodyOutputMessage(exchange, headers);
|
CachedBodyOutputMessage outputMessage = new CachedBodyOutputMessage(exchange, headers);
|
||||||
return bodyInserter.insert(outputMessage, new BodyInserterContext())
|
return bodyInserter.insert(outputMessage, new BodyInserterContext())
|
||||||
.then(Mono.defer(() -> {
|
.then(Mono.defer(() -> {
|
||||||
ServerHttpRequest decorator = decorate(exchange, headers, outputMessage);
|
ServerHttpRequest request = decorate(exchange, headers, outputMessage);
|
||||||
return chain.filter(exchange.mutate().request(decorator).build());
|
return chain.filter(exchange.mutate().request(request).build());
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static ServerHttpRequestDecorator decorate(ServerWebExchange exchange, HttpHeaders headers, CachedBodyOutputMessage outputMessage)
|
public static ServerHttpRequestDecorator decorate(ServerWebExchange exchange, HttpHeaders headers,
|
||||||
{
|
CachedBodyOutputMessage outputMessage) {
|
||||||
return new ServerHttpRequestDecorator(exchange.getRequest()) {
|
return new ServerHttpRequestDecorator(exchange.getRequest()) {
|
||||||
public HttpHeaders getHeaders()
|
@Override
|
||||||
{
|
public HttpHeaders getHeaders() {
|
||||||
long contentLength = headers.getContentLength();
|
long contentLength = headers.getContentLength();
|
||||||
HttpHeaders httpHeaders = new HttpHeaders();
|
HttpHeaders httpHeaders = new HttpHeaders();
|
||||||
httpHeaders.putAll(super.getHeaders());
|
httpHeaders.putAll(super.getHeaders());
|
||||||
if (contentLength > 0L)
|
if (contentLength > 0L) {
|
||||||
{
|
|
||||||
httpHeaders.setContentLength(contentLength);
|
httpHeaders.setContentLength(contentLength);
|
||||||
} else
|
} else {
|
||||||
{
|
|
||||||
httpHeaders.set("Transfer-Encoding", "chunked");
|
httpHeaders.set("Transfer-Encoding", "chunked");
|
||||||
}
|
}
|
||||||
return httpHeaders;
|
return httpHeaders;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Flux<DataBuffer> getBody()
|
@Override
|
||||||
{
|
public Flux<DataBuffer> getBody() {
|
||||||
return outputMessage.getBody();
|
return outputMessage.getBody();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -104,21 +109,21 @@ public class GatewayUtils {
|
|||||||
* @param <X>
|
* @param <X>
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public static <X extends RuntimeException> String getValue(String before, Supplier<List<String>> valueSupplier, Supplier<X> errorMsgSupplier)
|
public static <X extends RuntimeException> String getValue(String before, Supplier<List<String>> valueSupplier,
|
||||||
{
|
Supplier<X> errorMsgSupplier) {
|
||||||
if(StringUtils.hasText(before)) {
|
if (StringUtils.hasText(before)) {
|
||||||
return before;
|
return before;
|
||||||
}
|
}
|
||||||
final List<String> values = valueSupplier.get();
|
final List<String> values = valueSupplier.get();
|
||||||
if(CollectionUtil.isNotEmpty(values)) {
|
if (CollectionUtil.isNotEmpty(values)) {
|
||||||
for (String value : values) {
|
for (String value : values) {
|
||||||
if(StringUtils.hasText(value)) {
|
if (StringUtils.hasText(value)) {
|
||||||
before = value;
|
before = value;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(errorMsgSupplier != null) {
|
if (errorMsgSupplier != null) {
|
||||||
Assert.notBlank(before, errorMsgSupplier);
|
Assert.notBlank(before, errorMsgSupplier);
|
||||||
}
|
}
|
||||||
return before;
|
return before;
|
||||||
@ -126,15 +131,17 @@ public class GatewayUtils {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取必要值,可抛出异常
|
* 获取必要值,可抛出异常
|
||||||
|
*
|
||||||
* @param valueSupplier
|
* @param valueSupplier
|
||||||
* @param errorMsgSupplier
|
* @param errorMsgSupplier
|
||||||
* @param <T>
|
* @param <T>
|
||||||
* @param <X>
|
* @param <X>
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public static <T, X extends RuntimeException> T getRequiredValue(Supplier<T> valueSupplier, Supplier<X> errorMsgSupplier) {
|
public static <T, X extends RuntimeException> T getRequiredValue(Supplier<T> valueSupplier,
|
||||||
|
Supplier<X> errorMsgSupplier) {
|
||||||
final T value = valueSupplier.get();
|
final T value = valueSupplier.get();
|
||||||
if(Objects.nonNull(errorMsgSupplier)) {
|
if (Objects.nonNull(errorMsgSupplier)) {
|
||||||
Assert.notNull(value, errorMsgSupplier);
|
Assert.notNull(value, errorMsgSupplier);
|
||||||
}
|
}
|
||||||
return value;
|
return value;
|
||||||
@ -143,7 +150,8 @@ public class GatewayUtils {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取规则
|
* 获取规则
|
||||||
* @param ak AK
|
*
|
||||||
|
* @param ak AK
|
||||||
* @param routeId 路由ID
|
* @param routeId 路由ID
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
@ -154,11 +162,13 @@ public class GatewayUtils {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 断言是否超过结束时间
|
* 断言是否超过结束时间
|
||||||
|
*
|
||||||
* @param rule
|
* @param rule
|
||||||
* @param errorMsgSupplier
|
* @param errorMsgSupplier
|
||||||
* @param <X>
|
* @param <X>
|
||||||
*/
|
*/
|
||||||
public static <X extends RuntimeException> void isBefore(IscRule rule, Supplier<X> errorMsgSupplier) {
|
public static <X extends RuntimeException> void isBefore(IscRule rule, Supplier<X> errorMsgSupplier) {
|
||||||
Assert.isTrue(Objects.nonNull(rule.getExpire()) && Date.from(Instant.now()).before(rule.getExpire()), errorMsgSupplier);
|
Assert.isTrue(Objects.nonNull(rule.getExpire()) && Date.from(Instant.now()).before(rule.getExpire()),
|
||||||
|
errorMsgSupplier);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -21,7 +21,7 @@ spring:
|
|||||||
# 端口,默认为6379
|
# 端口,默认为6379
|
||||||
port: 6379
|
port: 6379
|
||||||
# 数据库索引
|
# 数据库索引
|
||||||
database: 0
|
database: 11
|
||||||
# 密码
|
# 密码
|
||||||
password:
|
password:
|
||||||
# 连接超时时间
|
# 连接超时时间
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user