本文主要是介绍spring-gateway filters添加自定义过滤器实现流程分析(可插拔),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
《spring-gatewayfilters添加自定义过滤器实现流程分析(可插拔)》:本文主要介绍spring-gatewayfilters添加自定义过滤器实现流程分析(可插拔),本文通过实例图...
需求背景
公司要求,通过公司网络代理访问的请求需要做请求隔离,即,通过特定代理IP访问的请求,需要除正常权限以外,还需要对请求路径,及特定路径下的请求参数做校验,通过校验正常访问,否则拒绝通过代理IP的请求。
需求拆解
1.针对特定的服务做限制;
2.特定的代理IP来源请求做校验;
3.特定的请求接口做限制;
4.特定的接口的特定的请求参数值做校验;
大致可以细分如上四点,在网关层统一实现需求,参考spring-gateway过滤器原理,个性化一个过滤器,按需配置即可;
设计流程及作用域
在配置网关路由策略时按需配置,灵活使用。
图2.1过滤器位置
自定义过滤器名称:InternalValidator
2.2spring-gateway使用配置示例
过滤器设计
1.name : InternalValidator 过滤器id (必须)
2.args : 过滤器参数 (个性化实现时必须)
2.1 paths: /api/**,/** 请求路径校验(通配符),前提条件1
2.2 limited-from: '172.24.173.62,172.24.172.252' 模拟代理IP地址,前提条件2
2.3 conditions: 条件过滤,这是个个性化配置集合
2.3.1 paths: /api/** 条件集合判断路径校验(通配符)
2.3.2 body-param: 'code' 请求体参数名{'code':'159300'}
2.3.3 body-value:'159300' 参数名对应的参数值
2.3.4 path-param: 'name' 路径参数名 ?name=zhangsan
2.3.5 path-value: 'zhangsan' 路径参数值
3.excludes:'/api/login/*' 排除路径(通配符)
逻辑处理
1.来自代理IP判断
3.1逻辑处理
逻辑流程
3.2流程处理
代码逻辑
1.名称必须是InternalValidator + GatewayFilterFactory
2.config服从驼峰命名转换 即limited-from : limitedFrom
3.对config映射属性必须要有set方法
@Component @Slf4j public class InternalValidatorGatewayFilterFactory extends AbstractGatewayFilterFactory<InternalValidatorGatewayFilterFactory.Config> implements Ordered { private final AntPathMatcher antPathMatcher = new AntPathMatcher(); public InternalValidatorGatewayFilterFactory() { super(Config.class); // 指定配置类 } @Override public GatewayFilter apply(Config config) { return (exchange, chain) -> { Set<String> limitedIps = config.getLimitedIps(); ServerHttpRequest request = exchange.getRequest(); //接口路径 String clientIp = IPHelp.getClientIp(request); if (!isLimitedIp(clientIp, limitedIps)) { return chain.filter(exchange); } String path = request.getPath().value(); Set<String> includesPaths = config.getIncludesPaths(); if (!pathMatch(path, includesPaths)) { log.info(" --> includesPaths: {}, 不包含: 请求路径:{},", includesPaths, path); return chain.filter(exchange); } Set<String> excludesPaths = config.getExcludesPaths(); if (pathMatch(path, excludesPaths)) { log.info(" --> excludesPaths: {}, contains: 请求路径:{},", excludesPaths, path); return chain.filter(exchange); } Map&China编程lt;String, Map<String, String>> pathParamValueMap = config.getPathParamValueMap(); Map<String, Map<String, String>> bodyParamValueMap = config.getBodyParamValueMap(); Map<String, String> bodyParamMap = getPathParamValueMap(path, bodyParamValueMap); Map<String, String> queryParamMap = getPathParamValueMap(path, pathParamValueMap); if ((bodyParamMap == null || bodyParamMap.isEmpty()) && ( queryParamMap == null || queryParamMap.isEmpty() )) { exchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN); return exchange.getResponse().setComplete(); } if (queryParamMap != null && !queryParamMap.isEmpty()) { MultiValueMap<String, String> queryParams = request.getQueryParams(); if (!queryParamMatch(queryParamMap, queryParams)) { log.info(" --> path: {}, bodyParamConditions: {}, queryParamMap: {},校验失败!", path, bodyParamMap, queryParamMap); exchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN); return exchange.getResponse().setComplete(); } if (bodyParamMap == null || bodyParamMap.isEmpty()) { return chain.filter(exchange); } } if (bodyParamMap != null && !bodyParamMap.isEmpty()) { String method = request.getMethodValue(); if ("POST".equals(method)) { return DataBufferUtils.join(exchange.getRequest().getBody()) .flatMap(dataBuffer -> { byte[] bytes = new byte[dataBuffer.readableByteCount()]; dataBuffer.read(bytes); www.chinasem.cntry { String bodyString = new String(bytes, "utf-8"); if (!bodyParamMatch(bodyParamMap, bodyString)) { exchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN); return exchange.getResponse().setComplete(); } } catch (UnsupportedEncodingException e) { } DataBufferUtils.release(dataBuffer); Flux<DataBuffer> cachedFlux = Flux.defer(() -> { DataBuffer buffer = exchange.getResponse().bufferFactory() .wrap(bytes); return Mono.just(buffer); }); ServerHttpRequest mutatedRequest = new ServerHttpRequestDecorator( exchange.getRequest()) { @Override public Flux<DataBuffer> getBody() { return cachedFlux; } }; js return chain.filter(exchange.mutate().request(mutatedRequest).build()); }); } } exchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN); return exchange.getResponse().setComplete(); }; } @Override public int getOrder() { return 2; } private Map<String, String> getPathParamValueMap(String path, Map<String, Map<String, String>> paramValueMap) { if (paramValueMap == null || paramValueMap.isEmpty()) { return null; } Map<String, String> res = new HashMap<>(); for (Map.Entry<String, Map<String, String>> paramValueEntry : paramValueMap.entrySet()) { String pathPredicate = paramValueEntry.getKey(); if (antPathMatcher.match(pathPredicate, path)) { Map<String, String> paramValue = paramValueEntry.getValue(); if (paramValue == null) { continue; } res.putAll(paramValue); } } return res; } private String extractParam(String jsonStr, String key) { try { int startIndex = jsonStr.indexOf("\"" + key + "\""); if (startIndex != -1) { int valueStart = jsonStr.indexOf(':', startIndex); int valueEnd = jsonStr.indexOf(',', valueStart); if (valueEnd == -1) { valueEnd = jsonStr.indexOf('}', valueStart); } if (valueStart != -1 && valueEnd != -1) { String valuePart = jsonStr.substring(valueStart + 1, valueEnd).trim(); return valuePart.replaceAll("\"", ""); } } } catch (Exception e) { // 可以记录日志 return null; } return null; } private boolean pathMatch(String path, Set<String> predicatePaths) { if (predicatePaths == null || predicatePaths.isEmpty()) { return false; } return predicatePaths.stream() .anyMatch(predicatePath -> antPathMatcher.match(predicatePath, path)); } private boolean paramMatch(Map<String, String> bodyParamMap, Map<String, String> queryParamMap, ServerHttpRequest request, ServerWebExchange exchange ) { return true; } private boolean bodyParamMatch(Map<String, String> bodyParamConditions, String bodyStr) { if (bodyStr == null) { return false; } for (Map.Entry<String, String> conditionMapEntry : bodyParamConditions.entrySet()) { String predicateParam = conditionMapEntry.getKey(); String predicateValue = conditionMapEntry.getValue(); String acValue = extractParam(bodyStr, predicateParam); if (!predicateValue.equals(acValue)) { log.info(" --> bodyParamMatch:::predicateParam:{}, predicateValue:{}, acValue:{},参数校验不通过!", predicateParam, predicateValue, acValue); return false; } } return true; } private boolean queryParamMatch(Map<String, String> conditionMap, MultiValueMap<String, String> queryParams) { for (Map.Entry<String, String> conditionMapEntry : conditionMap.entrySet()) { String predicateParam = conditionMapEntry.getKey(); String predicateValue = conditionMapEntry.getValue(); String acValue = queryParams.getFirst(predicateParam); if (!predicateValue.equals(acValue)) { log.info(" --> queryParamMatch:::predicateParam:{}, predicateValue:{}, acValue:{},参数校验不通过!", predicateParam, predicateValue, acValue); return false; } } return true; } private boolean isLimitedIp(String clientIp, Set<String> limitedIps) { if (clientIp == null || clientIp.isEmpty()) { log.warn(" --> clientIp:{} 为空!", clientIp); return true; } if (limitedIps == null || limitedIps.isEmpty()) { log.info(" --> limitedIps:{} 为空!", limitedIps); return false; } for (String limitedIp : limitedIps) { if (clientIp.contains(limitedIp)) { return true; } } return false; } // 配置类 public static class Config { private String paths = "/**"; private String limitedFrom; private List<ConditionConfig> conditions = new ArrayList<>(); private String excludes; pChina编程rivate Set<String> includesPaths; private Set<String> limitedIps = new HashSet<>(); private Set<String> excludesPaths = new HashSet<>(); private Map<String, Map<String, String>> bodyParamValueMap = new HashMap<>(); private Map<String, Map<String, String>> pathParamValueMap = new HashMap<>(); public void setPaths(String paths) { this.paths = paths; if (paths != null) { this.includesPaths = new HashSet<>(Arrays.asList(paths.split(","))); } } public void setLimitedFrom(String limitedFrom) { this.limitedFrom = limitedFrom; if (limitedFrom != null) { this.limitedIps = new HashSet<>(Arrays.asList(limitedFrom.split(","))); } } public void setConditions(List<ConditionConfig> conditions) { this.conditions = conditions; if (conditions != null && !conditions.isEmpty()) { for (ConditionConfig condition : conditions) { String conditionPaths = condition.getPaths(); String bodyParam = condition.getBodyParam(); String bodyValue = condition.getBodyValue(); String pathParam = condition.getPathParam(); String pathValue = condition.getPathValue(); if (conditionPaths != null) { if (bodyParam != null && bodyValue != null) { for (String path : conditionPaths.split(",")) { Map<String, String> bodyParamCondition = bodyjavascriptParamValueMap.get(path); if (bodyParamCondition == null) { bodyParamCondition = new HashMap<>(); } bodyParamCondition.put(bodyParam, bodyValue); bodyParamValueMap.put(path, bodyParamCondition); } } if (pathParam != null && pathValue != null) { for (String path : conditionPaths.split(",")) { Map<String, String> pathParamCondition = pathParamValueMap.get(path); if (pathParamCondition == null) { pathParamCondition = new HashMap<>(); } pathParamCondition.put(pathParam, pathValue); pathParamValueMap.put(path, pathParamCondition); } } } } } } public void setExcludes(String excludes) { this.excludes = excludes; if (excludes != null) { this.excludesPaths = new HashSet<>(Arrays.asList(excludes.split(","))); } } public Set<String> getIncludesPaths() { return includesPaths; } public Set<String> getLimitedIps() { return limitedIps; } public Set<String> getExcludesPaths() { return excludesPaths; } public Map<String, Map<String, String>> getBodyParamValueMap() { return bodyParamValueMap; } public Map<String, Map<String, String>> getPathParamValueMap() { return pathParamValueMap; } } @Data public static class ConditionConfig { private String paths; private String bodyParam; private String bodyValue; private String pathParam; private String pathValue; } }
借助了工具类IPHelp.Java
import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpHeaders; import org.springframework.http.server.reactive.ServerHttpRequest; import java.net.InetSocketAddress; import java.util.Arrays; import java.util.Collections; import java.util.List; @Slf4j public class IPHelp { private static final List<String> IP_HEADERS = Collections.unmodifiableList(Arrays.asList( "X-Forwarded-For", "x-forwarded-for", "xff", "x-real-ip", "Proxy-Client-IP", "WL-Proxy-Client-IP", "HTTP_CLIENT_IP", "HTTP_X_FORWARDED_FOR" )); public static String getClientIp(ServerHttpRequest request) { String resultIp = null; HttpHeaders headers = request.getHeaders(); for (String head : IP_HEADERS) { String ip = headers.getFirst(head); log.info(" --> IP_HEADER:{}, IP_VALUE:{}", head, ip); if (isValidIp(ip)) { resultIp = ip; break; } } if (resultIp == null && request.getRemoteAddress() != null) { InetSocketAddress remoteAddress = request.getRemoteAddress(); resultIp = remoteAddress.getAddress().getHostAddress(); log.info(" --> IP_HEADER: remoteAddress, IP_VALUE:{}", resultIp); } log.info(" --> getClientIp, IP_VALUE:{}", resultIp); return resultIp; } private static boolean isValidIp(String ip) { return ip != null && !ip.trim().isEmpty() && !"unknown".equalsIgnoreCase(ip.trim()); } }
RESPECT!
到此这篇关于spring-gateway filters添加自定义过滤器实现(可插拔)的文章就介绍到这了,更多相关spring-gateway filters自定义过滤器内容请搜索China编程(www.chinasem.cn)以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程China编程(www.chinasem.cn)!
这篇关于spring-gateway filters添加自定义过滤器实现流程分析(可插拔)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!