mirror of
https://gitee.com/dromara/RuoYi-Vue-Plus.git
synced 2026-09-20 02:08:15 +08:00
添加POST 添加/删除参数过滤器、限流器配置
This commit is contained in:
parent
98722a6b22
commit
b5b765238d
@ -1,15 +1,21 @@
|
||||
package com.ruoyi.gateway.config;
|
||||
|
||||
import com.ruoyi.gateway.config.provider.RedisRouteDefinitionRepository;
|
||||
import com.ruoyi.gateway.filter.factory.AddRequestParamGatewayFilterFactory;
|
||||
import com.ruoyi.gateway.filter.factory.RemoveRequestParamGatewayFilterFactory;
|
||||
import org.redisson.api.RedissonClient;
|
||||
import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
|
||||
import org.springframework.cloud.gateway.route.Route;
|
||||
import org.springframework.cloud.gateway.route.RouteDefinition;
|
||||
import org.springframework.cloud.gateway.route.RouteDefinitionRepository;
|
||||
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
|
||||
import org.springframework.data.redis.serializer.StringRedisSerializer;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
@ -52,4 +58,22 @@ public class GatewayConfig
|
||||
{
|
||||
return new RedisRouteDefinitionRepository(redissonClient);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public AddRequestParamGatewayFilterFactory addRequestParamGatewayFilterFactory()
|
||||
{
|
||||
return new AddRequestParamGatewayFilterFactory();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public RemoveRequestParamGatewayFilterFactory removeRequestParamGatewayFilterFactory()
|
||||
{
|
||||
return new RemoveRequestParamGatewayFilterFactory();
|
||||
}
|
||||
|
||||
@Bean("routeIdKeyResolver")
|
||||
public KeyResolver routeIdKeyResolver()
|
||||
{
|
||||
return exchange -> Mono.just(((Route) exchange.getAttributes().get(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR)).getId());
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,11 @@
|
||||
package com.ruoyi.gateway.exception;
|
||||
|
||||
/**
|
||||
* @author Wenchao Gong
|
||||
* @date 2021-10-04
|
||||
*/
|
||||
public class GatewayException extends RuntimeException {
|
||||
public GatewayException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,114 @@
|
||||
package com.ruoyi.gateway.filter.factory;
|
||||
|
||||
import cn.hutool.json.JSONObject;
|
||||
import com.ruoyi.gateway.exception.GatewayException;
|
||||
import com.ruoyi.gateway.utils.GatewayUtils;
|
||||
import org.springframework.cloud.gateway.filter.GatewayFilter;
|
||||
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
|
||||
import org.springframework.cloud.gateway.filter.factory.AbstractNameValueGatewayFilterFactory;
|
||||
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.reactive.function.server.HandlerStrategies;
|
||||
import org.springframework.web.reactive.function.server.ServerRequest;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
import static org.springframework.cloud.gateway.support.GatewayToStringStyler.filterToStringCreator;
|
||||
|
||||
/**
|
||||
* @author Wenchao Gong
|
||||
* @date 2021-10-04
|
||||
*/
|
||||
public class AddRequestParamGatewayFilterFactory extends AbstractNameValueGatewayFilterFactory {
|
||||
|
||||
@Override
|
||||
public GatewayFilter apply(NameValueConfig config)
|
||||
{
|
||||
return new GatewayFilter() {
|
||||
@Override
|
||||
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain)
|
||||
{
|
||||
final ServerHttpRequest request = exchange.getRequest();
|
||||
HttpMethod method = request.getMethod();
|
||||
if (HttpMethod.GET.equals(method))
|
||||
{
|
||||
URI uri = request.getURI();
|
||||
StringBuilder query = new StringBuilder();
|
||||
String originalQuery = uri.getRawQuery();
|
||||
|
||||
if (StringUtils.hasText(originalQuery))
|
||||
{
|
||||
query.append(originalQuery);
|
||||
if (originalQuery.charAt(originalQuery.length() - 1) != '&')
|
||||
{
|
||||
query.append('&');
|
||||
}
|
||||
}
|
||||
|
||||
String value = ServerWebExchangeUtils.expand(exchange, config.getValue());
|
||||
// TODO urlencode?
|
||||
query.append(config.getName());
|
||||
query.append('=');
|
||||
query.append(value);
|
||||
|
||||
try
|
||||
{
|
||||
URI newUri = UriComponentsBuilder.fromUri(uri).replaceQuery(query.toString()).build(false).toUri();
|
||||
return chain.filter(exchange.mutate().request(request.mutate().uri(newUri).build()).build());
|
||||
} catch (RuntimeException ex)
|
||||
{
|
||||
throw new IllegalStateException("Invalid URI query: \"" + query.toString() + "\"");
|
||||
}
|
||||
} else if (HttpMethod.POST.equals(method))
|
||||
{
|
||||
ServerRequest serverRequest = ServerRequest.create(exchange, HandlerStrategies.withDefaults().messageReaders());
|
||||
Mono<String> modifiedBody = serverRequest.bodyToMono(String.class)
|
||||
.flatMap(body -> {
|
||||
MediaType mediaType = request.getHeaders().getContentType();
|
||||
if (MediaType.APPLICATION_JSON.isCompatibleWith(mediaType))
|
||||
{
|
||||
JSONObject jsonObject = new JSONObject(body);
|
||||
jsonObject.set(config.getName(), config.getValue());
|
||||
return Mono.just(jsonObject.toString());
|
||||
} else if (MediaType.APPLICATION_FORM_URLENCODED.isCompatibleWith(mediaType))
|
||||
{
|
||||
StringBuilder newBody = new StringBuilder();
|
||||
if (org.springframework.util.StringUtils.hasText(body))
|
||||
{
|
||||
newBody.append(body);
|
||||
if (body.charAt(body.length() - 1) != '&')
|
||||
{
|
||||
newBody.append('&');
|
||||
}
|
||||
}
|
||||
String value = ServerWebExchangeUtils.expand(exchange, config.getValue());
|
||||
newBody.append(config.getName());
|
||||
newBody.append('=');
|
||||
newBody.append(value);
|
||||
return Mono.just(newBody.toString());
|
||||
}
|
||||
return Mono.empty();
|
||||
});
|
||||
return GatewayUtils.modifyBody(exchange, chain, modifiedBody);
|
||||
} else
|
||||
{
|
||||
throw new GatewayException(String.format("Method %s Not Supported!", method.name()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return filterToStringCreator(AddRequestParamGatewayFilterFactory.this)
|
||||
.append(config.getName(), config.getValue()).toString();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,104 @@
|
||||
package com.ruoyi.gateway.filter.factory;
|
||||
|
||||
import cn.hutool.json.JSONObject;
|
||||
import com.ruoyi.gateway.exception.GatewayException;
|
||||
import com.ruoyi.gateway.utils.GatewayUtils;
|
||||
import org.springframework.cloud.gateway.filter.GatewayFilter;
|
||||
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
|
||||
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
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.ServerRequest;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.springframework.cloud.gateway.support.GatewayToStringStyler.filterToStringCreator;
|
||||
import static org.springframework.util.CollectionUtils.unmodifiableMultiValueMap;
|
||||
|
||||
/**
|
||||
* @author Wenchao Gong
|
||||
* @date 2021-10-15
|
||||
*/
|
||||
public class RemoveRequestParamGatewayFilterFactory extends AbstractGatewayFilterFactory<AbstractGatewayFilterFactory.NameConfig> {
|
||||
|
||||
public RemoveRequestParamGatewayFilterFactory()
|
||||
{
|
||||
super(NameConfig.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> shortcutFieldOrder()
|
||||
{
|
||||
return Arrays.asList(NAME_KEY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GatewayFilter apply(NameConfig config)
|
||||
{
|
||||
return new GatewayFilter() {
|
||||
@Override
|
||||
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain)
|
||||
{
|
||||
ServerHttpRequest request = exchange.getRequest();
|
||||
HttpMethod method = request.getMethod();
|
||||
if (HttpMethod.GET.equals(method))
|
||||
{
|
||||
MultiValueMap<String, String> queryParams = new LinkedMultiValueMap<>(request.getQueryParams());
|
||||
queryParams.remove(config.getName());
|
||||
|
||||
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());
|
||||
} else if (HttpMethod.POST.equals(method))
|
||||
{
|
||||
ServerRequest serverRequest = ServerRequest.create(exchange, HandlerStrategies.withDefaults().messageReaders());
|
||||
Mono<String> modifiedBody = serverRequest.bodyToMono(String.class)
|
||||
.flatMap(body -> {
|
||||
MediaType mediaType = request.getHeaders().getContentType();
|
||||
if (MediaType.APPLICATION_JSON.isCompatibleWith(mediaType))
|
||||
{
|
||||
JSONObject jsonObject = new JSONObject(body);
|
||||
jsonObject.remove(config.getName());
|
||||
return Mono.just(jsonObject.toString());
|
||||
} else if (MediaType.APPLICATION_FORM_URLENCODED.isCompatibleWith(mediaType))
|
||||
{
|
||||
if (StringUtils.hasText(body))
|
||||
{
|
||||
return Mono.just(Arrays.stream(body.split("&"))
|
||||
.filter(str -> config.getName().equals(str.split("=")[0]))
|
||||
.collect(Collectors.joining("&")));
|
||||
}
|
||||
return Mono.just(body);
|
||||
}
|
||||
return Mono.empty();
|
||||
});
|
||||
return GatewayUtils.modifyBody(exchange, chain, modifiedBody);
|
||||
} else
|
||||
{
|
||||
throw new GatewayException(String.format("Method %s Not Supported!", method.name()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return filterToStringCreator(RemoveRequestParamGatewayFilterFactory.this)
|
||||
.append("name", config.getName()).toString();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,61 @@
|
||||
package com.ruoyi.gateway.utils;
|
||||
|
||||
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
|
||||
import org.springframework.cloud.gateway.filter.factory.rewrite.CachedBodyOutputMessage;
|
||||
import org.springframework.cloud.gateway.support.BodyInserterContext;
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
|
||||
import org.springframework.web.reactive.function.BodyInserter;
|
||||
import org.springframework.web.reactive.function.BodyInserters;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
* @author Wenchao Gong
|
||||
* @date 2021-10-15
|
||||
*/
|
||||
public class GatewayUtils {
|
||||
|
||||
public static Mono<Void> modifyBody(ServerWebExchange exchange, GatewayFilterChain chain, Mono<String> publisher)
|
||||
{
|
||||
BodyInserter bodyInserter = BodyInserters.fromPublisher(publisher, String.class);
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.putAll(exchange.getRequest().getHeaders());
|
||||
headers.remove(HttpHeaders.CONTENT_LENGTH);
|
||||
CachedBodyOutputMessage outputMessage = new CachedBodyOutputMessage(exchange, headers);
|
||||
return bodyInserter.insert(outputMessage, new BodyInserterContext())
|
||||
.then(Mono.defer(() -> {
|
||||
ServerHttpRequest decorator = decorate(exchange, headers, outputMessage);
|
||||
return chain.filter(exchange.mutate().request(decorator).build());
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
public static ServerHttpRequestDecorator decorate(ServerWebExchange exchange, HttpHeaders headers, CachedBodyOutputMessage outputMessage)
|
||||
{
|
||||
return new ServerHttpRequestDecorator(exchange.getRequest()) {
|
||||
public HttpHeaders getHeaders()
|
||||
{
|
||||
long contentLength = headers.getContentLength();
|
||||
HttpHeaders httpHeaders = new HttpHeaders();
|
||||
httpHeaders.putAll(super.getHeaders());
|
||||
if (contentLength > 0L)
|
||||
{
|
||||
httpHeaders.setContentLength(contentLength);
|
||||
} else
|
||||
{
|
||||
httpHeaders.set("Transfer-Encoding", "chunked");
|
||||
}
|
||||
return httpHeaders;
|
||||
}
|
||||
|
||||
public Flux<DataBuffer> getBody()
|
||||
{
|
||||
return outputMessage.getBody();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -33,6 +33,7 @@ public class RouteUtils {
|
||||
* Gateway 虚拟路径前缀
|
||||
*/
|
||||
public static final String PATH_PREFIX = "/proxy";
|
||||
public static final String ACCESS_KEY_NAME = "ak";
|
||||
private static RedissonClient client = SpringUtils.getBean(RedissonClient.class);
|
||||
|
||||
/**
|
||||
@ -151,7 +152,7 @@ public class RouteUtils {
|
||||
|
||||
IscPredicateDefinition queryPredicate = new IscPredicateDefinition();
|
||||
queryPredicate.setName("Query");
|
||||
queryPredicate.getArgs().put("param", "ak");
|
||||
queryPredicate.getArgs().put("param", ACCESS_KEY_NAME);
|
||||
|
||||
IscPredicateDefinition pathPredicate = new IscPredicateDefinition();
|
||||
pathPredicate.setName("Path");
|
||||
@ -165,23 +166,34 @@ public class RouteUtils {
|
||||
stripPrefixFilter.getArgs().put("parts", "1");
|
||||
filters.add(stripPrefixFilter);
|
||||
|
||||
IscFilterDefinition removeRequestParameterFilter = new IscFilterDefinition();
|
||||
removeRequestParameterFilter.setName("RemoveRequestParameter");
|
||||
removeRequestParameterFilter.getArgs().put("name", "ak");
|
||||
filters.add(removeRequestParameterFilter);
|
||||
final Long quotaSeconds = appService.getQuotaSeconds();
|
||||
if (Objects.nonNull(quotaSeconds))
|
||||
{
|
||||
IscFilterDefinition requestRateLimiterFilter = new IscFilterDefinition();
|
||||
requestRateLimiterFilter.setName("RequestRateLimiter");
|
||||
requestRateLimiterFilter.getArgs().put("redis-rate-limiter.replenishRate", String.valueOf(quotaSeconds));
|
||||
requestRateLimiterFilter.getArgs().put("redis-rate-limiter.burstCapacity", String.valueOf(quotaSeconds << 1));
|
||||
requestRateLimiterFilter.getArgs().put("key-resolver", "#{@routeIdKeyResolver}");
|
||||
filters.add(requestRateLimiterFilter);
|
||||
}
|
||||
|
||||
IscFilterDefinition removeRequestParamFilter = new IscFilterDefinition();
|
||||
removeRequestParamFilter.setName("RemoveRequestParam");
|
||||
removeRequestParamFilter.getArgs().put("name", ACCESS_KEY_NAME);
|
||||
filters.add(removeRequestParamFilter);
|
||||
|
||||
String hiddenParams = service.getHiddenParams();
|
||||
if(StringUtils.isNotBlank(hiddenParams)) {
|
||||
Map<String, Object> paramsMap = JsonUtils.parseMap(hiddenParams);
|
||||
Iterator<Map.Entry<String, Object>> it = paramsMap.entrySet().iterator();
|
||||
while (it.hasNext()) {
|
||||
IscFilterDefinition addRequestParameterFilter = new IscFilterDefinition();
|
||||
Map<String, String> args = addRequestParameterFilter.getArgs();
|
||||
addRequestParameterFilter.setName("AddRequestParameter");
|
||||
IscFilterDefinition addRequestParamFilter = new IscFilterDefinition();
|
||||
Map<String, String> args = addRequestParamFilter.getArgs();
|
||||
addRequestParamFilter.setName("AddRequestParam");
|
||||
Map.Entry<String, Object> param = it.next();
|
||||
args.put("name", param.getKey());
|
||||
args.put("value", String.valueOf(param.getValue()));
|
||||
filters.add(addRequestParameterFilter);
|
||||
filters.add(addRequestParamFilter);
|
||||
}
|
||||
}
|
||||
|
||||
@ -193,7 +205,6 @@ public class RouteUtils {
|
||||
|
||||
//其他信息
|
||||
Map<String, Object> metadata = route.getMetadata();
|
||||
metadata.put("secondsLimit", toString(appService.getQuotaSeconds()));
|
||||
metadata.put("minutesLimit", toString(appService.getQuotaMinutes()));
|
||||
metadata.put("hoursLimit", toString(appService.getQuotaHours()));
|
||||
metadata.put("daysLimit", toString(appService.getQuotaDays()));
|
||||
|
||||
@ -16,7 +16,11 @@
|
||||
</description>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.jsoup</groupId>
|
||||
<artifactId>jsoup</artifactId>
|
||||
<version>1.13.1</version>
|
||||
</dependency>
|
||||
<!-- 通用工具-->
|
||||
<dependency>
|
||||
<groupId>com.ruoyi</groupId>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user