diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/constant/IscConstants.java b/ruoyi-common/src/main/java/com/ruoyi/common/constant/IscConstants.java index 6ca156762..fe3c523f7 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/constant/IscConstants.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/constant/IscConstants.java @@ -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:"; } diff --git a/ruoyi-extend/ruoyi-isc-gateway/src/main/java/com/ruoyi/gateway/config/GatewayConfig.java b/ruoyi-extend/ruoyi-isc-gateway/src/main/java/com/ruoyi/gateway/config/GatewayConfig.java index 98b6e4f92..0836e7c70 100644 --- a/ruoyi-extend/ruoyi-isc-gateway/src/main/java/com/ruoyi/gateway/config/GatewayConfig.java +++ b/ruoyi-extend/ruoyi-isc-gateway/src/main/java/com/ruoyi/gateway/config/GatewayConfig.java @@ -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(); } } diff --git a/ruoyi-extend/ruoyi-isc-gateway/src/main/java/com/ruoyi/gateway/config/provider/RedisRouteDefinitionRepository.java b/ruoyi-extend/ruoyi-isc-gateway/src/main/java/com/ruoyi/gateway/config/provider/RedisRouteDefinitionRepository.java index 17d4c1dcc..ebdb94caf 100644 --- a/ruoyi-extend/ruoyi-isc-gateway/src/main/java/com/ruoyi/gateway/config/provider/RedisRouteDefinitionRepository.java +++ b/ruoyi-extend/ruoyi-isc-gateway/src/main/java/com/ruoyi/gateway/config/provider/RedisRouteDefinitionRepository.java @@ -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) diff --git a/ruoyi-extend/ruoyi-isc-gateway/src/main/java/com/ruoyi/gateway/filter/CustomerGlobalFilter.java b/ruoyi-extend/ruoyi-isc-gateway/src/main/java/com/ruoyi/gateway/filter/CustomerGlobalFilter.java new file mode 100644 index 000000000..ec6f5d4ba --- /dev/null +++ b/ruoyi-extend/ruoyi-isc-gateway/src/main/java/com/ruoyi/gateway/filter/CustomerGlobalFilter.java @@ -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 filter(ServerWebExchange exchange, GatewayFilterChain chain) { + final Route route = exchange.getRequiredAttribute(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR); + final Map 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 queryParams = new LinkedMultiValueMap<>(request.getQueryParams()); + handleRule(headerAk, () -> queryParams.get(accessKeyName), route, request, accessKeyName); + queryParams.remove(accessKeyName); + handleHiddenParams(route, queryParams, (json, map) -> { + Iterator> iterator = json.entrySet().iterator(); + while (iterator.hasNext()) { + Map.Entry 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 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> iterator = json.entrySet().iterator(); + while (iterator.hasNext()) { + Map.Entry 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 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 params = stream.filter(param -> !accessKeyName.equals(param[0])).collect(Collectors.toList()); + handleHiddenParams(route, params, (json, list) -> { + Iterator> iterator = json.entrySet().iterator(); + while (iterator.hasNext()) { + Map.Entry 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> 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 + */ + private void handleHiddenParams(Route route, U result, BiConsumer 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); + } +} diff --git a/ruoyi-extend/ruoyi-isc-gateway/src/main/java/com/ruoyi/gateway/filter/factory/AddRequestParamGatewayFilterFactory.java b/ruoyi-extend/ruoyi-isc-gateway/src/main/java/com/ruoyi/gateway/filter/factory/AddRequestParamGatewayFilterFactory.java deleted file mode 100644 index 9d20f6839..000000000 --- a/ruoyi-extend/ruoyi-isc-gateway/src/main/java/com/ruoyi/gateway/filter/factory/AddRequestParamGatewayFilterFactory.java +++ /dev/null @@ -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 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 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(); - } - }; - } - -} diff --git a/ruoyi-extend/ruoyi-isc-gateway/src/main/java/com/ruoyi/gateway/filter/factory/RemoveRequestParamGatewayFilterFactory.java b/ruoyi-extend/ruoyi-isc-gateway/src/main/java/com/ruoyi/gateway/filter/factory/RemoveRequestParamGatewayFilterFactory.java deleted file mode 100644 index 403214bf9..000000000 --- a/ruoyi-extend/ruoyi-isc-gateway/src/main/java/com/ruoyi/gateway/filter/factory/RemoveRequestParamGatewayFilterFactory.java +++ /dev/null @@ -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 { - - public RemoveRequestParamGatewayFilterFactory() - { - super(NameConfig.class); - } - - @Override - public List shortcutFieldOrder() - { - return Arrays.asList(NAME_KEY); - } - - @Override - public GatewayFilter apply(NameConfig config) - { - return new GatewayFilter() { - @Override - public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) - { - ServerHttpRequest request = exchange.getRequest(); - HttpMethod method = request.getMethod(); - if (HttpMethod.GET.equals(method)) - { - MultiValueMap 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 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(); - } - }; - } -} diff --git a/ruoyi-extend/ruoyi-isc-gateway/src/main/java/com/ruoyi/gateway/utils/GatewayUtils.java b/ruoyi-extend/ruoyi-isc-gateway/src/main/java/com/ruoyi/gateway/utils/GatewayUtils.java index eefd3033a..9cbfa0d20 100644 --- a/ruoyi-extend/ruoyi-isc-gateway/src/main/java/com/ruoyi/gateway/utils/GatewayUtils.java +++ b/ruoyi-extend/ruoyi-isc-gateway/src/main/java/com/ruoyi/gateway/utils/GatewayUtils.java @@ -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 modifyBody(ServerWebExchange exchange, GatewayFilterChain chain, Mono 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> valueSupplier) { + return getValue(before, valueSupplier, null); + } + + /** + * 从 values 中获取第一个不为空 的值 + * + * @param before 获取之前的值 如果不为空直接返回 + * @param valueSupplier 值列表生产者 + * @param errorMsgSupplier 异常信息生产者 + * @param + * @return + */ + public static String getValue(String before, Supplier> valueSupplier, Supplier errorMsgSupplier) + { + if(StringUtils.hasText(before)) { + return before; + } + final List 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 + * @param + * @return + */ + public static T getRequiredValue(Supplier valueSupplier, Supplier 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 map = client.getMap(KEY_RULES, RULE_CODES_INSTANCE); + return map.get(ak + ':' + routeId); + } + + /** + * 断言是否超过结束时间 + * @param rule + * @param errorMsgSupplier + * @param + */ + public static void isBefore(IscRule rule, Supplier errorMsgSupplier) { + Assert.isTrue(Objects.nonNull(rule.getExpire()) && Date.from(Instant.now()).before(rule.getExpire()), errorMsgSupplier); + } } diff --git a/ruoyi-extend/ruoyi-isc-gateway/src/main/java/com/ruoyi/gateway/utils/beans/IscRule.java b/ruoyi-extend/ruoyi-isc-gateway/src/main/java/com/ruoyi/gateway/utils/beans/IscRule.java new file mode 100644 index 000000000..23e205f20 --- /dev/null +++ b/ruoyi-extend/ruoyi-isc-gateway/src/main/java/com/ruoyi/gateway/utils/beans/IscRule.java @@ -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; +} diff --git a/ruoyi-isc/src/main/java/com/ruoyi/isc/service/IIscAppServiceService.java b/ruoyi-isc/src/main/java/com/ruoyi/isc/service/IIscAppServiceService.java index d19089f90..f3d74ece8 100644 --- a/ruoyi-isc/src/main/java/com/ruoyi/isc/service/IIscAppServiceService.java +++ b/ruoyi-isc/src/main/java/com/ruoyi/isc/service/IIscAppServiceService.java @@ -70,7 +70,7 @@ public interface IIscAppServiceService extends IServicePlus getAppServiceListByIds(Collection ids); /** - * 刷新路由信息 + * 刷新规则信息(服务对应AK) */ - void refreshRoutes(); + void refreshRules(); } diff --git a/ruoyi-isc/src/main/java/com/ruoyi/isc/service/IIscServiceService.java b/ruoyi-isc/src/main/java/com/ruoyi/isc/service/IIscServiceService.java index 2ff145ec1..48a675940 100644 --- a/ruoyi-isc/src/main/java/com/ruoyi/isc/service/IIscServiceService.java +++ b/ruoyi-isc/src/main/java/com/ruoyi/isc/service/IIscServiceService.java @@ -90,4 +90,9 @@ public interface IIscServiceService extends IServicePlus 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 list = list(Wrappers.lambdaQuery() .eq(IscAppService::getEnabled, UserConstants.NORMAL) @@ -210,33 +208,23 @@ public class IscAppServiceServiceImpl extends ServicePlusImpl serviceIds = list.stream().map(IscAppService::getServiceId).collect(Collectors.toSet()); - Map serviceMap = serviceService.list(Wrappers.lambdaQuery() - .select(IscService::getServiceId, IscService::getServiceAddr, IscService::getHiddenParams, IscService::getRequestMethod) - .in(IscService::getServiceId, serviceIds)).stream() - .collect(Collectors.toMap(IscService::getServiceId, Function.identity())); Set applicationIds = list.stream().map(IscAppService::getApplicationId).collect(Collectors.toSet()); Map accessKeyMap = applicationService.list(Wrappers.lambdaQuery() .select(IscApplication::getApplicationId, IscApplication::getAccessKey) .in(IscApplication::getApplicationId, applicationIds)).stream() .collect(Collectors.toMap(IscApplication::getApplicationId, IscApplication::getAccessKey)); - List routes = new ArrayList<>(); + List 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); } /** diff --git a/ruoyi-isc/src/main/java/com/ruoyi/isc/service/impl/IscServiceServiceImpl.java b/ruoyi-isc/src/main/java/com/ruoyi/isc/service/impl/IscServiceServiceImpl.java index 1ecff6f25..afda1230b 100644 --- a/ruoyi-isc/src/main/java/com/ruoyi/isc/service/impl/IscServiceServiceImpl.java +++ b/ruoyi-isc/src/main/java/com/ruoyi/isc/service/impl/IscServiceServiceImpl.java @@ -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 serviceList = list(Wrappers.lambdaQuery() + .select(IscService::getServiceId, IscService::getServiceAddr, IscService::getHiddenParams, IscService::getRequestMethod) + .eq(IscService::getStatus, IscConstants.AUDIT_PASS)); + List routes = new ArrayList<>(); + for (IscService service : serviceList) + { + routes.add(RouteUtils.generateRoute(service)); + } + RouteUtils.refreshRoute(routes); + } } diff --git a/ruoyi-isc/src/main/java/com/ruoyi/isc/utils/RouteUtils.java b/ruoyi-isc/src/main/java/com/ruoyi/isc/utils/RouteUtils.java index 220e97468..77d259ac5 100644 --- a/ruoyi-isc/src/main/java/com/ruoyi/isc/utils/RouteUtils.java +++ b/ruoyi-isc/src/main/java/com/ruoyi/isc/utils/RouteUtils.java @@ -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 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 paramsMap = JsonUtils.parseMap(hiddenParams); - Iterator> it = paramsMap.entrySet().iterator(); - while (it.hasNext()) { - IscFilterDefinition addRequestParamFilter = new IscFilterDefinition(); - Map args = addRequestParamFilter.getArgs(); - addRequestParamFilter.setName("AddRequestParam"); - Map.Entry 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 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 rules) + { + Map ruleMap = rules.stream().collect(Collectors.toMap(IscRule::getId, Function.identity(), (o1, o2) -> o2)); + final RMap 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 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 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); } } diff --git a/ruoyi-isc/src/main/java/com/ruoyi/isc/utils/beans/GatewayRoute.java b/ruoyi-isc/src/main/java/com/ruoyi/isc/utils/beans/GatewayRoute.java deleted file mode 100644 index bfd071015..000000000 --- a/ruoyi-isc/src/main/java/com/ruoyi/isc/utils/beans/GatewayRoute.java +++ /dev/null @@ -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 -{ -} diff --git a/ruoyi-isc/src/main/java/com/ruoyi/isc/utils/beans/IscRule.java b/ruoyi-isc/src/main/java/com/ruoyi/isc/utils/beans/IscRule.java new file mode 100644 index 000000000..3d55f6ed6 --- /dev/null +++ b/ruoyi-isc/src/main/java/com/ruoyi/isc/utils/beans/IscRule.java @@ -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; +}