spring-gateway filters添加自定义过滤器实现流程分析(可插拔)

本文主要是介绍spring-gateway filters添加自定义过滤器实现流程分析(可插拔),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

《spring-gatewayfilters添加自定义过滤器实现流程分析(可插拔)》:本文主要介绍spring-gatewayfilters添加自定义过滤器实现流程分析(可插拔),本文通过实例图...

需求背景

公司要求,通过公司网络代理访问的请求需要做请求隔离,即,通过特定代理IP访问的请求,需要除正常权限以外,还需要对请求路径,及特定路径下的请求参数做校验,通过校验正常访问,否则拒绝通过代理IP的请求。

需求拆解

1.针对特定的服务做限制;

2.特定的代理IP来源请求做校验;

3.特定的请求接口做限制;

4.特定的接口的特定的请求参数值做校验;

大致可以细分如上四点,在网关层统一实现需求,参考spring-gateway过滤器原理,个性化一个过滤器,按需配置即可;

设计流程及作用域

在配置网关路由策略时按需配置,灵活使用。

spring-gateway filters添加自定义过滤器实现流程分析(可插拔)

图2.1过滤器位置

自定义过滤器名称:InternalValidator

spring-gateway filters添加自定义过滤器实现流程分析(可插拔)

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判断

spring-gateway filters添加自定义过滤器实现流程分析(可插拔)

3.1逻辑处理

逻辑流程

spring-gateway filters添加自定义过滤器实现流程分析(可插拔)

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添加自定义过滤器实现流程分析(可插拔)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1154825

相关文章

SpringBoot实现文件记录日志及日志文件自动归档和压缩

《SpringBoot实现文件记录日志及日志文件自动归档和压缩》Logback是Java日志框架,通过Logger收集日志并经Appender输出至控制台、文件等,SpringBoot配置logbac... 目录1、什么是Logback2、SpringBoot实现文件记录日志,日志文件自动归档和压缩2.1、

Python实现pdf电子发票信息提取到excel表格

《Python实现pdf电子发票信息提取到excel表格》这篇文章主要为大家详细介绍了如何使用Python实现pdf电子发票信息提取并保存到excel表格,文中的示例代码讲解详细,感兴趣的小伙伴可以跟... 目录应用场景详细代码步骤总结优化应用场景电子发票信息提取系统主要应用于以下场景:企业财务部门:需

基于Python实现智能天气提醒助手

《基于Python实现智能天气提醒助手》这篇文章主要来和大家分享一个实用的Python天气提醒助手开发方案,这个工具可以方便地集成到青龙面板或其他调度框架中使用,有需要的小伙伴可以参考一下... 目录项目概述核心功能技术实现1. 天气API集成2. AI建议生成3. 消息推送环境配置使用方法完整代码项目特点

MQTT SpringBoot整合实战教程

《MQTTSpringBoot整合实战教程》:本文主要介绍MQTTSpringBoot整合实战教程,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考... 目录MQTT-SpringBoot创建简单 SpringBoot 项目导入必须依赖增加MQTT相关配置编写

使用Python获取JS加载的数据的多种实现方法

《使用Python获取JS加载的数据的多种实现方法》在当今的互联网时代,网页数据的动态加载已经成为一种常见的技术手段,许多现代网站通过JavaScript(JS)动态加载内容,这使得传统的静态网页爬取... 目录引言一、动态 网页与js加载数据的原理二、python爬取JS加载数据的方法(一)分析网络请求1

Spring Security介绍及配置实现代码

《SpringSecurity介绍及配置实现代码》SpringSecurity是一个功能强大的Java安全框架,它提供了全面的安全认证(Authentication)和授权(Authorizatio... 目录简介Spring Security配置配置实现代码简介Spring Security是一个功能强

SpringCloud使用Nacos 配置中心实现配置自动刷新功能使用

《SpringCloud使用Nacos配置中心实现配置自动刷新功能使用》SpringCloud项目中使用Nacos作为配置中心可以方便开发及运维人员随时查看配置信息,及配置共享,并且Nacos支持配... 目录前言一、Nacos中集中配置方式?二、使用步骤1.使用$Value 注解2.使用@Configur

Java 中的跨域问题解决方法

《Java中的跨域问题解决方法》跨域问题本质上是浏览器的一种安全机制,与Java本身无关,但Java后端开发者需要理解其来源以便正确解决,下面给大家介绍Java中的跨域问题解决方法,感兴趣的朋友一起... 目录1、Java 中跨域问题的来源1.1. 浏览器同源策略(Same-Origin Policy)1.

Java 关键字transient与注解@Transient的区别用途解析

《Java关键字transient与注解@Transient的区别用途解析》在Java中,transient是一个关键字,用于声明一个字段不会被序列化,这篇文章给大家介绍了Java关键字transi... 在Java中,transient 是一个关键字,用于声明一个字段不会被序列化。当一个对象被序列化时,被

在Java中基于Geotools对PostGIS数据库的空间查询实践教程

《在Java中基于Geotools对PostGIS数据库的空间查询实践教程》本文将深入探讨这一实践,从连接配置到复杂空间查询操作,包括点查询、区域范围查询以及空间关系判断等,全方位展示如何在Java环... 目录前言一、相关技术背景介绍1、评价对象AOI2、数据处理流程二、对AOI空间范围查询实践1、空间查