diff --git a/blade-gateway/src/main/java/org/springblade/gateway/config/RouterFunctionConfiguration.java b/blade-gateway/src/main/java/org/springblade/gateway/config/RouterFunctionConfiguration.java index 700bf99..e2ef767 100644 --- a/blade-gateway/src/main/java/org/springblade/gateway/config/RouterFunctionConfiguration.java +++ b/blade-gateway/src/main/java/org/springblade/gateway/config/RouterFunctionConfiguration.java @@ -18,20 +18,13 @@ package org.springblade.gateway.config; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springblade.gateway.filter.GatewayFilter; import org.springblade.gateway.props.AuthProperties; +import org.springblade.gateway.props.RequestProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpMethod; -import org.springframework.http.HttpStatus; -import org.springframework.http.server.reactive.ServerHttpRequest; -import org.springframework.http.server.reactive.ServerHttpResponse; -import org.springframework.web.cors.reactive.CorsUtils; -import org.springframework.web.server.ServerWebExchange; import org.springframework.web.server.WebFilter; -import org.springframework.web.server.WebFilterChain; -import reactor.core.publisher.Mono; /** * 路由配置信息 @@ -41,41 +34,15 @@ import reactor.core.publisher.Mono; @Slf4j @Configuration(proxyBeanMethods = false) @AllArgsConstructor -@EnableConfigurationProperties({AuthProperties.class}) +@EnableConfigurationProperties({AuthProperties.class, RequestProperties.class}) public class RouterFunctionConfiguration { /** - * 这里为支持的请求头,如果有自定义的header字段请自己添加 - */ - private static final String ALLOWED_HEADERS = "X-Requested-With, Tenant-Id, Blade-Auth, Content-Type, Authorization, credential, X-XSRF-TOKEN, token, username, client, knfie4j-gateway-request, knife4j-gateway-code, request-origion"; - private static final String ALLOWED_METHODS = "GET,POST,PUT,DELETE,OPTIONS,HEAD"; - private static final String ALLOWED_ORIGIN = "*"; - private static final String ALLOWED_EXPOSE = "*"; - private static final String MAX_AGE = "18000L"; - - /** - * 跨域配置 + * 全局配置 */ @Bean - public WebFilter corsFilter() { - return (ServerWebExchange ctx, WebFilterChain chain) -> { - ServerHttpRequest request = ctx.getRequest(); - if (CorsUtils.isCorsRequest(request)) { - ServerHttpResponse response = ctx.getResponse(); - HttpHeaders headers = response.getHeaders(); - headers.add("Access-Control-Allow-Headers", ALLOWED_HEADERS); - headers.add("Access-Control-Allow-Methods", ALLOWED_METHODS); - headers.add("Access-Control-Allow-Origin", ALLOWED_ORIGIN); - headers.add("Access-Control-Expose-Headers", ALLOWED_EXPOSE); - headers.add("Access-Control-Max-Age", MAX_AGE); - headers.add("Access-Control-Allow-Credentials", "true"); - if (request.getMethod() == HttpMethod.OPTIONS) { - response.setStatusCode(HttpStatus.OK); - return Mono.empty(); - } - } - return chain.filter(ctx); - }; + public WebFilter gatewayFilter(RequestProperties requestProperties) { + return new GatewayFilter(requestProperties); } } diff --git a/blade-gateway/src/main/java/org/springblade/gateway/filter/GatewayFilter.java b/blade-gateway/src/main/java/org/springblade/gateway/filter/GatewayFilter.java new file mode 100644 index 0000000..3867f9a --- /dev/null +++ b/blade-gateway/src/main/java/org/springblade/gateway/filter/GatewayFilter.java @@ -0,0 +1,161 @@ +/** + * Copyright (c) 2018-2099, Chill Zhuang 庄骞 (bladejava@qq.com). + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springblade.gateway.filter; + +import lombok.RequiredArgsConstructor; +import org.springblade.gateway.props.RequestProperties; +import org.springframework.core.Ordered; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.http.server.reactive.ServerHttpResponse; +import org.springframework.lang.NonNull; +import org.springframework.util.AntPathMatcher; +import org.springframework.util.PatternMatchUtils; +import org.springframework.web.cors.reactive.CorsUtils; +import org.springframework.web.server.ServerWebExchange; +import org.springframework.web.server.WebFilter; +import org.springframework.web.server.WebFilterChain; +import reactor.core.publisher.Mono; + +import java.util.List; +import java.util.Objects; + +/** + * 全局拦截器 + * + * @author Chill + */ +@RequiredArgsConstructor +public class GatewayFilter implements WebFilter, Ordered { + + /** + * 请求配置 + */ + private final RequestProperties requestProperties; + /** + * 路径匹配 + */ + private final AntPathMatcher antPathMatcher = new AntPathMatcher(); + + /** + * 默认拦截地址 + */ + private final List defaultBlockUrl = List.of("/**/actuator/**", "/health/**"); + /** + * 默认白名单 + */ + private final List defaultWhiteList = List.of("127.0.0.1", "172.30.*.*", "192.168.*.*", "10.*.*.*", "0:0:0:0:0:0:0:1"); + /** + * 默认提示信息 + */ + private final static String DEFAULT_MESSAGE = "当前请求被拒绝,请联系管理员!"; + + /** + * 这里为支持的请求头,如果有自定义的header字段请自己添加 + */ + private static final String ALLOWED_HEADERS = "X-Requested-With, Tenant-Id, Blade-Auth, Content-Type, Authorization, credential, X-XSRF-TOKEN, token, username, client, knfie4j-gateway-request, knife4j-gateway-code, request-origion"; + private static final String ALLOWED_METHODS = "GET,POST,PUT,DELETE,OPTIONS,HEAD"; + private static final String ALLOWED_ORIGIN = "*"; + private static final String ALLOWED_EXPOSE = "*"; + private static final String MAX_AGE = "18000L"; + + + @NonNull + @Override + public Mono filter(@NonNull ServerWebExchange exchange, @NonNull WebFilterChain chain) { + ServerHttpRequest request = exchange.getRequest(); + // 处理跨域请求 + if (CorsUtils.isCorsRequest(request)) { + ServerHttpResponse response = exchange.getResponse(); + HttpHeaders headers = response.getHeaders(); + headers.add("Access-Control-Allow-Headers", ALLOWED_HEADERS); + headers.add("Access-Control-Allow-Methods", ALLOWED_METHODS); + headers.add("Access-Control-Allow-Origin", ALLOWED_ORIGIN); + headers.add("Access-Control-Expose-Headers", ALLOWED_EXPOSE); + headers.add("Access-Control-Max-Age", MAX_AGE); + headers.add("Access-Control-Allow-Credentials", "true"); + if (request.getMethod() == HttpMethod.OPTIONS) { + response.setStatusCode(HttpStatus.OK); + return Mono.empty(); + } + } + // 处理黑白名单与拦截请求 + if (requestProperties.getEnabled()) { + String path = request.getPath().value(); + String ip = Objects.requireNonNull(request.getRemoteAddress()).getHostString(); + if (isRequestBlock(path, ip)) { + throw new RuntimeException(DEFAULT_MESSAGE); + } + } + return chain.filter(exchange); + } + + + /** + * 是否白名单 + * + * @param ip ip地址 + * @return boolean + */ + private boolean isWhiteList(String ip) { + List whiteList = requestProperties.getWhiteList(); + String[] defaultWhiteIps = defaultWhiteList.toArray(new String[0]); + String[] whiteIps = whiteList.toArray(new String[0]); + return PatternMatchUtils.simpleMatch(defaultWhiteIps, ip) || PatternMatchUtils.simpleMatch(whiteIps, ip); + } + + /** + * 是否黑名单 + * + * @param ip ip地址 + * @return boolean + */ + private boolean isBlackList(String ip) { + List blackList = requestProperties.getBlackList(); + String[] blackIps = blackList.toArray(new String[0]); + return PatternMatchUtils.simpleMatch(blackIps, ip); + } + + /** + * 是否禁用请求访问 + * + * @param path 请求路径 + * @return boolean + */ + private boolean isRequestBlock(String path) { + List blockUrl = requestProperties.getBlockUrl(); + return defaultBlockUrl.stream().anyMatch(pattern -> antPathMatcher.match(pattern, path)) || + blockUrl.stream().anyMatch(pattern -> antPathMatcher.match(pattern, path)); + } + + /** + * 是否拦截请求 + * + * @param path 请求路径 + * @param ip ip地址 + * @return boolean + */ + private boolean isRequestBlock(String path, String ip) { + return (isRequestBlock(path) && !isWhiteList(ip)) || isBlackList(ip); + } + + @Override + public int getOrder() { + return Ordered.HIGHEST_PRECEDENCE; + } +} diff --git a/blade-gateway/src/main/java/org/springblade/gateway/props/RequestProperties.java b/blade-gateway/src/main/java/org/springblade/gateway/props/RequestProperties.java new file mode 100644 index 0000000..3250e00 --- /dev/null +++ b/blade-gateway/src/main/java/org/springblade/gateway/props/RequestProperties.java @@ -0,0 +1,58 @@ +/** + * Copyright (c) 2018-2099, Chill Zhuang 庄骞 (bladejava@qq.com). + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springblade.gateway.props; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; + +import java.util.ArrayList; +import java.util.List; + +/** + * Request配置类 + * + * @author Chill + */ +@Data +@ConfigurationProperties("blade.request") +public class RequestProperties { + + /** + * 开启自定义request + */ + private Boolean enabled = true; + + /** + * 放行url + */ + private List skipUrl = new ArrayList<>(); + + /** + * 禁用url + */ + private List blockUrl = new ArrayList<>(); + + /** + * 白名单,支持通配符,例如:10.20.0.8*、10.20.0.* + */ + private List whiteList = new ArrayList<>(); + + /** + * 黑名单,支持通配符,例如:10.20.0.8*、10.20.0.* + */ + private List blackList = new ArrayList<>(); + +}