mirror of
https://gitee.com/dromara/RuoYi-Vue-Plus.git
synced 2026-09-20 02:08:15 +08:00
限流规则修改
This commit is contained in:
parent
2a6479c743
commit
dd46fa932f
@ -26,5 +26,10 @@ public class IscConstants {
|
||||
/**
|
||||
* Gateway 路由信息Redis Key
|
||||
*/
|
||||
public static final String KEY_ROUTES = "ROUTES::";
|
||||
public static final String KEY_ROUTES = "ROUTES:";
|
||||
|
||||
/**
|
||||
* Gateway 服务对应AK规则
|
||||
*/
|
||||
public static final String KEY_RULES = "RULES:";
|
||||
}
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
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 com.ruoyi.gateway.filter.CustomerGlobalFilter;
|
||||
import org.redisson.api.RedissonClient;
|
||||
import org.springframework.cloud.gateway.filter.GlobalFilter;
|
||||
import org.springframework.cloud.gateway.route.RouteDefinition;
|
||||
import org.springframework.cloud.gateway.route.RouteDefinitionRepository;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@ -56,14 +56,8 @@ public class GatewayConfig
|
||||
}
|
||||
|
||||
@Bean
|
||||
public AddRequestParamGatewayFilterFactory addRequestParamGatewayFilterFactory()
|
||||
public GlobalFilter customerGlobalFilter()
|
||||
{
|
||||
return new AddRequestParamGatewayFilterFactory();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public RemoveRequestParamGatewayFilterFactory removeRequestParamGatewayFilterFactory()
|
||||
{
|
||||
return new RemoveRequestParamGatewayFilterFactory();
|
||||
return new CustomerGlobalFilter();
|
||||
}
|
||||
}
|
||||
|
||||
@ -20,7 +20,7 @@ import reactor.core.publisher.Mono;
|
||||
public class RedisRouteDefinitionRepository implements RouteDefinitionRepository
|
||||
{
|
||||
public static final Codec ROUTE_CODES_INSTANCE = new TypedJsonJacksonCodec(String.class, RouteDefinition.class);
|
||||
public static final String KEY_ROUTES = "ROUTES::";
|
||||
public static final String KEY_ROUTES = "ROUTES:";
|
||||
private final RedissonClient redissonClient;
|
||||
|
||||
public RedisRouteDefinitionRepository(RedissonClient redissonClient)
|
||||
|
||||
@ -0,0 +1,170 @@
|
||||
package com.ruoyi.gateway.filter;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.ruoyi.gateway.utils.GatewayUtils;
|
||||
import com.ruoyi.gateway.utils.beans.IscRule;
|
||||
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
|
||||
import org.springframework.cloud.gateway.filter.GlobalFilter;
|
||||
import org.springframework.cloud.gateway.route.Route;
|
||||
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
|
||||
import org.springframework.core.Ordered;
|
||||
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.MultiValueMapAdapter;
|
||||
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.*;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.springframework.util.CollectionUtils.unmodifiableMultiValueMap;
|
||||
|
||||
/**
|
||||
* 自定义全局过滤器,操作如下:
|
||||
* 1.ak 是否存在
|
||||
* 2.是否到期
|
||||
* 3.是否限流
|
||||
* 4.删除参数ak
|
||||
* 5.添加隐藏参数
|
||||
*
|
||||
* @author Wenchao Gong
|
||||
* @date 2021-10-15
|
||||
*/
|
||||
public class CustomerGlobalFilter implements GlobalFilter, Ordered {
|
||||
@Override
|
||||
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
|
||||
final Route route = exchange.getRequiredAttribute(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR);
|
||||
final Map<String, Object> metadata = route.getMetadata();
|
||||
final ServerHttpRequest request = exchange.getRequest();
|
||||
//ak 是否存在
|
||||
final HttpMethod httpMethod = request.getMethod();
|
||||
final String accessKeyName = String.valueOf(metadata.get(GatewayUtils.CONFIG_ACCESS_KEY_NAME_KEY));
|
||||
String headerAk = GatewayUtils.getValue(null, () -> request.getHeaders().get(accessKeyName));
|
||||
if (HttpMethod.GET.equals(httpMethod)) {
|
||||
final MultiValueMap<String, String> queryParams = new LinkedMultiValueMap<>(request.getQueryParams());
|
||||
handleRule(headerAk, () -> queryParams.get(accessKeyName), route, request, accessKeyName);
|
||||
queryParams.remove(accessKeyName);
|
||||
handleHiddenParams(route, queryParams, (json, map) -> {
|
||||
Iterator<Map.Entry<String, Object>> iterator = json.entrySet().iterator();
|
||||
while (iterator.hasNext()) {
|
||||
Map.Entry<String, Object> next = iterator.next();
|
||||
Object value;
|
||||
if (Objects.isNull(value = next.getValue()) || StrUtil.isBlank(value.toString())) {
|
||||
map.add(next.getKey(), StrUtil.EMPTY);
|
||||
} 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());
|
||||
} else if (HttpMethod.POST.equals(httpMethod)) {
|
||||
final ServerRequest serverRequest = ServerRequest.create(exchange, HandlerStrategies.withDefaults().messageReaders());
|
||||
final Mono<String> modifiedBody = serverRequest.bodyToMono(String.class);
|
||||
modifiedBody.flatMap(body -> {
|
||||
MediaType mediaType = request.getHeaders().getContentType();
|
||||
if (MediaType.APPLICATION_JSON.equals(mediaType)) {
|
||||
JSONObject jsonObj = JSONUtil.parseObj(body);
|
||||
String ak = handleRule(headerAk, () -> Arrays.asList(jsonObj.get(accessKeyName, String.class,
|
||||
true)), route, request, accessKeyName);
|
||||
jsonObj.remove(accessKeyName);
|
||||
handleHiddenParams(route, jsonObj, (json, map) -> {
|
||||
Iterator<Map.Entry<String, Object>> iterator = json.entrySet().iterator();
|
||||
while (iterator.hasNext()) {
|
||||
Map.Entry<String, Object> next = iterator.next();
|
||||
map.set(next.getKey(), next.getValue());
|
||||
}
|
||||
});
|
||||
return Mono.just(jsonObj.toString());
|
||||
} else if (MediaType.APPLICATION_FORM_URLENCODED.equals(mediaType)) {
|
||||
if (StringUtils.hasText(body)) {
|
||||
final Stream<String[]> stream = Arrays.stream(body.split("&")).map(param -> param.split("="));
|
||||
handleRule(headerAk, () -> stream.filter(param -> param.length > 0 &&
|
||||
accessKeyName.equals(param[0])).map(param -> param[1]).collect(Collectors.toList()),
|
||||
route, request, accessKeyName);
|
||||
final List<String[]> params = stream.filter(param -> !accessKeyName.equals(param[0])).collect(Collectors.toList());
|
||||
handleHiddenParams(route, params, (json, list) -> {
|
||||
Iterator<Map.Entry<String, Object>> iterator = json.entrySet().iterator();
|
||||
while (iterator.hasNext()) {
|
||||
Map.Entry<String, Object> next = iterator.next();
|
||||
list.add(new String[]{next.getKey(), next.getValue().toString()});
|
||||
}
|
||||
});
|
||||
return Mono.just(params.stream().map(param -> param[0] + '=' + param[1]).collect(Collectors.joining("&")));
|
||||
}
|
||||
}
|
||||
return Mono.empty();
|
||||
});
|
||||
return GatewayUtils.modifyBody(exchange, chain, modifiedBody);
|
||||
}
|
||||
return chain.filter(exchange);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理规则
|
||||
*
|
||||
* @param headerAk
|
||||
* @param valueSupplier
|
||||
* @param route
|
||||
* @param request
|
||||
* @param accessKeyName
|
||||
* @return
|
||||
*/
|
||||
private String handleRule(String headerAk, Supplier<List<String>> valueSupplier, Route route,
|
||||
ServerHttpRequest request, String accessKeyName) {
|
||||
//获取AK
|
||||
final String ak = GatewayUtils.getValue(headerAk, valueSupplier, () -> new RuntimeException("AK 不存在"));
|
||||
//获取规则
|
||||
final IscRule rule = GatewayUtils.getRequiredValue(() -> GatewayUtils.getRule(ak, route.getId()),
|
||||
() -> new RuntimeException("AK异常"));
|
||||
//是否到期
|
||||
GatewayUtils.isBefore(rule, () -> new RuntimeException("AK已过期"));
|
||||
//TODO 限流
|
||||
|
||||
//如果header中有AK,则删除
|
||||
if (Objects.nonNull(headerAk)) {
|
||||
request.getHeaders().remove(accessKeyName);
|
||||
}
|
||||
return ak;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理隐藏参数
|
||||
*
|
||||
* @param route
|
||||
* @param result
|
||||
* @param mapper
|
||||
* @param <U>
|
||||
*/
|
||||
private <U> void handleHiddenParams(Route route, U result, BiConsumer<JSONObject, U> mapper) {
|
||||
final Object obj = route.getMetadata().get(GatewayUtils.CONFIG_ADD_PARAM_KEY);
|
||||
if (Objects.isNull(obj)) {
|
||||
return;
|
||||
}
|
||||
final JSONObject json = JSONUtil.parseObj(obj.toString());
|
||||
if (json.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
mapper.accept(json, result);
|
||||
}
|
||||
}
|
||||
@ -1,114 +0,0 @@
|
||||
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();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,104 +0,0 @@
|
||||
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();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -1,5 +1,13 @@
|
||||
package com.ruoyi.gateway.utils;
|
||||
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.extra.spring.SpringUtil;
|
||||
import com.ruoyi.gateway.utils.beans.IscRule;
|
||||
import org.redisson.api.RMap;
|
||||
import org.redisson.api.RedissonClient;
|
||||
import org.redisson.client.codec.Codec;
|
||||
import org.redisson.codec.TypedJsonJacksonCodec;
|
||||
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
|
||||
import org.springframework.cloud.gateway.filter.factory.rewrite.CachedBodyOutputMessage;
|
||||
import org.springframework.cloud.gateway.support.BodyInserterContext;
|
||||
@ -7,18 +15,35 @@ 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.util.StringUtils;
|
||||
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;
|
||||
|
||||
import java.sql.Date;
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* @author Wenchao Gong
|
||||
* @date 2021-10-15
|
||||
*/
|
||||
public class GatewayUtils {
|
||||
|
||||
public static final String CONFIG_ACCESS_KEY_NAME_KEY = "accessKeyName";
|
||||
public static final String CONFIG_ADD_PARAM_KEY = "addParam";
|
||||
/**
|
||||
* Gateway 服务对应AK规则
|
||||
*/
|
||||
public static final String KEY_RULES = "RULES:";
|
||||
|
||||
public static final Codec RULE_CODES_INSTANCE = new TypedJsonJacksonCodec(String.class, IscRule.class);
|
||||
private static RedissonClient client = SpringUtil.getBean(RedissonClient.class);
|
||||
|
||||
public static Mono<Void> modifyBody(ServerWebExchange exchange, GatewayFilterChain chain, Mono<String> publisher)
|
||||
{
|
||||
BodyInserter bodyInserter = BodyInserters.fromPublisher(publisher, String.class);
|
||||
@ -58,4 +83,82 @@ public class GatewayUtils {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 从 values 中获取第一个不为空 的值
|
||||
*
|
||||
* @param before 获取之前的值 如果不为空直接返回
|
||||
* @param valueSupplier 值列表生产者
|
||||
* @return
|
||||
*/
|
||||
public static String getValue(String before, Supplier<List<String>> valueSupplier) {
|
||||
return getValue(before, valueSupplier, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从 values 中获取第一个不为空 的值
|
||||
*
|
||||
* @param before 获取之前的值 如果不为空直接返回
|
||||
* @param valueSupplier 值列表生产者
|
||||
* @param errorMsgSupplier 异常信息生产者
|
||||
* @param <X>
|
||||
* @return
|
||||
*/
|
||||
public static <X extends RuntimeException> String getValue(String before, Supplier<List<String>> valueSupplier, Supplier<X> errorMsgSupplier)
|
||||
{
|
||||
if(StringUtils.hasText(before)) {
|
||||
return before;
|
||||
}
|
||||
final List<String> values = valueSupplier.get();
|
||||
if(CollectionUtil.isNotEmpty(values)) {
|
||||
for (String value : values) {
|
||||
if(StringUtils.hasText(value)) {
|
||||
before = value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(errorMsgSupplier != null) {
|
||||
Assert.notBlank(before, errorMsgSupplier);
|
||||
}
|
||||
return before;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取必要值,可抛出异常
|
||||
* @param valueSupplier
|
||||
* @param errorMsgSupplier
|
||||
* @param <T>
|
||||
* @param <X>
|
||||
* @return
|
||||
*/
|
||||
public static <T, X extends RuntimeException> T getRequiredValue(Supplier<T> valueSupplier, Supplier<X> errorMsgSupplier) {
|
||||
final T value = valueSupplier.get();
|
||||
if(Objects.nonNull(errorMsgSupplier)) {
|
||||
Assert.notNull(value, errorMsgSupplier);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取规则
|
||||
* @param ak AK
|
||||
* @param routeId 路由ID
|
||||
* @return
|
||||
*/
|
||||
public static IscRule getRule(String ak, String routeId) {
|
||||
final RMap<String, IscRule> map = client.getMap(KEY_RULES, RULE_CODES_INSTANCE);
|
||||
return map.get(ak + ':' + routeId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 断言是否超过结束时间
|
||||
* @param rule
|
||||
* @param errorMsgSupplier
|
||||
* @param <X>
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,26 @@
|
||||
package com.ruoyi.gateway.utils.beans;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 网关 规则信息(服务对应AK处理规则)
|
||||
* @author Wechao Gong
|
||||
* @date 2021-10-16
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
public class IscRule implements Serializable
|
||||
{
|
||||
@JsonIgnore
|
||||
private String id;
|
||||
private Date expire;
|
||||
private Long daysLimit;
|
||||
private Long hoursLimit;
|
||||
private Long minutesLimit;
|
||||
private Long secondsLimit;
|
||||
}
|
||||
@ -70,7 +70,7 @@ public interface IIscAppServiceService extends IServicePlus<IscAppService, IscAp
|
||||
List<IscAppService> getAppServiceListByIds(Collection<Long> ids);
|
||||
|
||||
/**
|
||||
* 刷新路由信息
|
||||
* 刷新规则信息(服务对应AK)
|
||||
*/
|
||||
void refreshRoutes();
|
||||
void refreshRules();
|
||||
}
|
||||
|
||||
@ -90,4 +90,9 @@ public interface IIscServiceService extends IServicePlus<IscService, IscServiceV
|
||||
* @param bo 审核业务对象
|
||||
*/
|
||||
void checkAuditBO(IscAuditBo bo);
|
||||
|
||||
/**
|
||||
* 刷新路由信息
|
||||
*/
|
||||
void refreshRoutes();
|
||||
}
|
||||
|
||||
@ -159,7 +159,6 @@ public class IscAppServiceApplyServiceImpl extends ServicePlusImpl<IscAppService
|
||||
Assert.notNull(appService, () -> new ServiceException("申请信息不存在"));
|
||||
|
||||
IscAppService updateData = null;
|
||||
IscRouteDefinition route = null;
|
||||
IscService service = null;
|
||||
if(IscConstants.AUDIT_PASS.equals(bo.getStatus())) {
|
||||
service = serviceService.getById(appService.getServiceId());
|
||||
@ -175,8 +174,7 @@ public class IscAppServiceApplyServiceImpl extends ServicePlusImpl<IscAppService
|
||||
if(Objects.nonNull(service)) {
|
||||
IscAppService dbAppService = appServiceService.getById(apply.getAppServiceId());
|
||||
IscApplication application = applicationService.getById(dbAppService.getApplicationId());
|
||||
route = RouteUtils.generateRoute(dbAppService, service, application.getAccessKey());
|
||||
RouteUtils.saveRoute(route);
|
||||
RouteUtils.saveRule(RouteUtils.generateRule(dbAppService, application.getAccessKey()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -243,8 +241,7 @@ public class IscAppServiceApplyServiceImpl extends ServicePlusImpl<IscAppService
|
||||
renewalDuration = Objects.nonNull(log) ? log.getRenewalDuration() : 1;
|
||||
}
|
||||
updateData.setEndTime(DateUtils.beginOfDay(DateUtils.addMonths(DateUtils.getNowDate(), renewalDuration)));
|
||||
updateData.setVirtualAddr(RouteUtils.genVirtualAddrPath(appService.getApplicationId(),
|
||||
appService.getServiceId(), service.getServiceAddr()));
|
||||
updateData.setVirtualAddr(RouteUtils.genVirtualAddrPath(service.getServiceAddr()));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -20,7 +20,6 @@ import com.ruoyi.common.utils.StringUtils;
|
||||
import com.ruoyi.isc.domain.IscAppService;
|
||||
import com.ruoyi.isc.domain.IscAppServiceApply;
|
||||
import com.ruoyi.isc.domain.IscApplication;
|
||||
import com.ruoyi.isc.domain.IscService;
|
||||
import com.ruoyi.isc.domain.bo.IscAppServiceApplyBo;
|
||||
import com.ruoyi.isc.domain.bo.IscAppServiceBo;
|
||||
import com.ruoyi.isc.domain.vo.IscAppServiceVo;
|
||||
@ -30,14 +29,13 @@ import com.ruoyi.isc.service.IIscAppServiceService;
|
||||
import com.ruoyi.isc.service.IIscApplicationService;
|
||||
import com.ruoyi.isc.service.IIscServiceService;
|
||||
import com.ruoyi.isc.utils.RouteUtils;
|
||||
import com.ruoyi.isc.utils.beans.IscRouteDefinition;
|
||||
import com.ruoyi.isc.utils.beans.IscRule;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.annotation.Resource;
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
@ -60,7 +58,7 @@ public class IscAppServiceServiceImpl extends ServicePlusImpl<IscAppServiceMappe
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
refreshRoutes();
|
||||
refreshRules();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -201,7 +199,7 @@ public class IscAppServiceServiceImpl extends ServicePlusImpl<IscAppServiceMappe
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refreshRoutes()
|
||||
public void refreshRules()
|
||||
{
|
||||
List<IscAppService> list = list(Wrappers.<IscAppService>lambdaQuery()
|
||||
.eq(IscAppService::getEnabled, UserConstants.NORMAL)
|
||||
@ -210,33 +208,23 @@ public class IscAppServiceServiceImpl extends ServicePlusImpl<IscAppServiceMappe
|
||||
if(CollectionUtil.isEmpty(list)) {
|
||||
return;
|
||||
}
|
||||
Set<Long> serviceIds = list.stream().map(IscAppService::getServiceId).collect(Collectors.toSet());
|
||||
Map<Long, IscService> serviceMap = serviceService.list(Wrappers.<IscService>lambdaQuery()
|
||||
.select(IscService::getServiceId, IscService::getServiceAddr, IscService::getHiddenParams, IscService::getRequestMethod)
|
||||
.in(IscService::getServiceId, serviceIds)).stream()
|
||||
.collect(Collectors.toMap(IscService::getServiceId, Function.identity()));
|
||||
Set<Long> applicationIds = list.stream().map(IscAppService::getApplicationId).collect(Collectors.toSet());
|
||||
Map<Long, String> accessKeyMap = applicationService.list(Wrappers.<IscApplication>lambdaQuery()
|
||||
.select(IscApplication::getApplicationId, IscApplication::getAccessKey)
|
||||
.in(IscApplication::getApplicationId, applicationIds)).stream()
|
||||
.collect(Collectors.toMap(IscApplication::getApplicationId, IscApplication::getAccessKey));
|
||||
List<IscRouteDefinition> routes = new ArrayList<>();
|
||||
List<IscRule> routes = new ArrayList<>();
|
||||
|
||||
for (IscAppService appService : list)
|
||||
{
|
||||
IscService service = serviceMap.get(appService.getServiceId());
|
||||
if(Objects.isNull(service)) {
|
||||
log.error("路由初始化失败: id:[{}],服务[{}]信息不存在!", appService.getAppServiceId(), appService.getServiceId());
|
||||
continue;
|
||||
}
|
||||
String ak = accessKeyMap.get(appService.getApplicationId());
|
||||
if(StringUtils.isBlank(ak)) {
|
||||
log.error("路由初始化失败: id:[{}],ak[{}]信息不存在!", appService.getAppServiceId(), appService.getApplicationId());
|
||||
continue;
|
||||
}
|
||||
routes.add(RouteUtils.generateRoute(appService, service, ak));
|
||||
routes.add(RouteUtils.generateRule(appService, ak));
|
||||
}
|
||||
RouteUtils.refreshRoute(routes);
|
||||
RouteUtils.refreshRules(routes);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -25,8 +25,11 @@ import com.ruoyi.isc.domain.vo.IscServiceVo;
|
||||
import com.ruoyi.isc.mapper.IscServiceMapper;
|
||||
import com.ruoyi.isc.service.IIscServiceCateService;
|
||||
import com.ruoyi.isc.service.IIscServiceService;
|
||||
import com.ruoyi.isc.utils.RouteUtils;
|
||||
import com.ruoyi.isc.utils.beans.IscRouteDefinition;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.annotation.Resource;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
@ -43,6 +46,11 @@ public class IscServiceServiceImpl extends ServicePlusImpl<IscServiceMapper, Isc
|
||||
@Resource
|
||||
private IIscServiceCateService cateService;
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
refreshRoutes();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IscServiceVo queryById(Long serviceId)
|
||||
{
|
||||
@ -195,4 +203,18 @@ public class IscServiceServiceImpl extends ServicePlusImpl<IscServiceMapper, Isc
|
||||
throw new ServiceException("审核状态异常");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refreshRoutes()
|
||||
{
|
||||
final List<IscService> serviceList = list(Wrappers.<IscService>lambdaQuery()
|
||||
.select(IscService::getServiceId, IscService::getServiceAddr, IscService::getHiddenParams, IscService::getRequestMethod)
|
||||
.eq(IscService::getStatus, IscConstants.AUDIT_PASS));
|
||||
List<IscRouteDefinition> routes = new ArrayList<>();
|
||||
for (IscService service : serviceList)
|
||||
{
|
||||
routes.add(RouteUtils.generateRoute(service));
|
||||
}
|
||||
RouteUtils.refreshRoute(routes);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,15 +2,13 @@ package com.ruoyi.isc.utils;
|
||||
|
||||
import com.ruoyi.common.constant.IscConstants;
|
||||
import com.ruoyi.common.exception.ServiceException;
|
||||
import com.ruoyi.common.utils.DateUtils;
|
||||
import com.ruoyi.common.utils.JsonUtils;
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
import com.ruoyi.common.utils.spring.SpringUtils;
|
||||
import com.ruoyi.isc.domain.IscAppService;
|
||||
import com.ruoyi.isc.domain.IscService;
|
||||
import com.ruoyi.isc.utils.beans.IscFilterDefinition;
|
||||
import com.ruoyi.isc.utils.beans.IscPredicateDefinition;
|
||||
import com.ruoyi.isc.utils.beans.IscRouteDefinition;
|
||||
import com.ruoyi.isc.utils.beans.IscRule;
|
||||
import org.redisson.api.RMap;
|
||||
import org.redisson.api.RedissonClient;
|
||||
import org.redisson.client.codec.Codec;
|
||||
@ -18,7 +16,10 @@ import org.redisson.codec.TypedJsonJacksonCodec;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@ -29,6 +30,7 @@ import java.util.stream.Collectors;
|
||||
public class RouteUtils {
|
||||
|
||||
public static final Codec ROUTE_CODES_INSTANCE = new TypedJsonJacksonCodec(String.class, IscRouteDefinition.class);
|
||||
public static final Codec RULE_CODES_INSTANCE = new TypedJsonJacksonCodec(String.class, IscRule.class);
|
||||
/**
|
||||
* Gateway 虚拟路径前缀
|
||||
*/
|
||||
@ -39,12 +41,10 @@ public class RouteUtils {
|
||||
/**
|
||||
* 获取虚拟URL Path 部分
|
||||
*
|
||||
* @param applicationId 应用ID
|
||||
* @param serviceId 服务Id
|
||||
* @param serviceAddr 服务地址
|
||||
* @param serviceAddr 服务地址
|
||||
* @return 虚拟地址Path
|
||||
*/
|
||||
public static String genVirtualAddrPath(Long applicationId, Long serviceId, String serviceAddr)
|
||||
public static String genVirtualAddrPath(String serviceAddr)
|
||||
{
|
||||
return PATH_PREFIX + getPathUri(serviceAddr);
|
||||
}
|
||||
@ -133,15 +133,13 @@ public class RouteUtils {
|
||||
/**
|
||||
* 生成 路由信息
|
||||
*
|
||||
* @param appService 应用服务信息
|
||||
* @param service 服务信息
|
||||
* @param ak AK
|
||||
* @return 路由信息
|
||||
*/
|
||||
public static IscRouteDefinition generateRoute(IscAppService appService, IscService service, String ak)
|
||||
public static IscRouteDefinition generateRoute(IscService service)
|
||||
{
|
||||
IscRouteDefinition route = new IscRouteDefinition();
|
||||
route.setId(String.valueOf(appService.getAppServiceId()));
|
||||
route.setId(String.valueOf(service.getServiceId()));
|
||||
final String serviceAddr = service.getServiceAddr();
|
||||
route.setUri(getURI(serviceAddr.substring(0, serviceAddr.length() - getPathUri(serviceAddr).length())));
|
||||
|
||||
@ -150,14 +148,10 @@ public class RouteUtils {
|
||||
methodPredicate.setName("Method");
|
||||
methodPredicate.getArgs().put("methods", service.getRequestMethod());
|
||||
|
||||
IscPredicateDefinition queryPredicate = new IscPredicateDefinition();
|
||||
queryPredicate.setName("Query");
|
||||
queryPredicate.getArgs().put("param", ACCESS_KEY_NAME);
|
||||
|
||||
IscPredicateDefinition pathPredicate = new IscPredicateDefinition();
|
||||
pathPredicate.setName("Path");
|
||||
pathPredicate.getArgs().put("pattern", appService.getVirtualAddr());
|
||||
route.setPredicates(Arrays.asList(methodPredicate, queryPredicate, pathPredicate));
|
||||
pathPredicate.getArgs().put("pattern", genVirtualAddrPath(service.getServiceAddr()));
|
||||
route.setPredicates(Arrays.asList(methodPredicate, pathPredicate));
|
||||
|
||||
//过滤器
|
||||
List<IscFilterDefinition> filters = new ArrayList<>();
|
||||
@ -166,26 +160,6 @@ public class RouteUtils {
|
||||
stripPrefixFilter.getArgs().put("parts", "1");
|
||||
filters.add(stripPrefixFilter);
|
||||
|
||||
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 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(addRequestParamFilter);
|
||||
}
|
||||
}
|
||||
|
||||
IscFilterDefinition retryFilter = new IscFilterDefinition();
|
||||
retryFilter.setName("Retry");
|
||||
retryFilter.getArgs().put("retries", "1");
|
||||
@ -194,23 +168,79 @@ 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()));
|
||||
metadata.put("expire", toString(appService.getEndTime()));
|
||||
metadata.put("accessKeyName", ACCESS_KEY_NAME);
|
||||
metadata.put("addParam", service.getHiddenParams());
|
||||
return route;
|
||||
}
|
||||
|
||||
/**
|
||||
* 对象转字符串
|
||||
* 生成 服务对应AK调用规则
|
||||
*
|
||||
* @param obj 具体属性
|
||||
* @param appService
|
||||
* @param accessKey
|
||||
* @return
|
||||
*/
|
||||
public static String toString(Object obj)
|
||||
public static IscRule generateRule(IscAppService appService, String accessKey)
|
||||
{
|
||||
return Objects.isNull(obj) ? StringUtils.EMPTY : obj instanceof Date ?
|
||||
DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS, (Date) obj) : String.valueOf(obj);
|
||||
IscRule rule = new IscRule();
|
||||
rule.setId(accessKey + ':' + appService.getServiceId());
|
||||
rule.setExpire(appService.getEndTime());
|
||||
rule.setDaysLimit(appService.getQuotaDays());
|
||||
rule.setHoursLimit(appService.getQuotaHours());
|
||||
rule.setMinutesLimit(appService.getQuotaMinutes());
|
||||
rule.setSecondsLimit(appService.getQuotaSeconds());
|
||||
return rule;
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新规则信息 先清空再批量新增
|
||||
*
|
||||
* @param rules 规则信息
|
||||
* @return 是否成功
|
||||
*/
|
||||
public static boolean refreshRules(List<IscRule> rules)
|
||||
{
|
||||
Map<String, IscRule> ruleMap = rules.stream().collect(Collectors.toMap(IscRule::getId, Function.identity(), (o1, o2) -> o2));
|
||||
final RMap<String, IscRule> map = client.getMap(IscConstants.KEY_RULES, RULE_CODES_INSTANCE);
|
||||
map.clear();
|
||||
map.putAll(ruleMap);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存规则信息
|
||||
*
|
||||
* @param rule 规则信息
|
||||
* @return 是否成功
|
||||
*/
|
||||
public static boolean saveRule(IscRule rule)
|
||||
{
|
||||
final RMap<String, IscRule> map = client.getMap(IscConstants.KEY_RULES, RULE_CODES_INSTANCE);
|
||||
map.put(rule.getId(), rule);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除规则信息
|
||||
*
|
||||
* @param ruleId 规则ID
|
||||
* @return 是否成功
|
||||
*/
|
||||
public static boolean deleteRule(String ruleId)
|
||||
{
|
||||
final RMap<String, IscRule> map = client.getMap(IscConstants.KEY_RULES, RULE_CODES_INSTANCE);
|
||||
map.remove(ruleId);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新规则信息
|
||||
*
|
||||
* @param rule 规则信息
|
||||
* @return 是否成功
|
||||
*/
|
||||
public static boolean updateRule(IscRule rule)
|
||||
{
|
||||
return saveRule(rule);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,16 +0,0 @@
|
||||
package com.ruoyi.isc.utils.beans;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* @author Wechao Gong
|
||||
* @date 2021/9-14
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
public class GatewayRoute implements Serializable
|
||||
{
|
||||
}
|
||||
@ -0,0 +1,26 @@
|
||||
package com.ruoyi.isc.utils.beans;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 网关 规则信息(服务对应AK处理规则)
|
||||
* @author Wechao Gong
|
||||
* @date 2021-10-16
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
public class IscRule implements Serializable
|
||||
{
|
||||
@JsonIgnore
|
||||
private String id;
|
||||
private Date expire;
|
||||
private Long daysLimit;
|
||||
private Long hoursLimit;
|
||||
private Long minutesLimit;
|
||||
private Long secondsLimit;
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user