gateway网关接口请求的校验方式


Posted in Java/Android onJuly 15, 2021

gateway网关token的校验

再加入gateway网关之后,我们在后台服务的许多校验操作都可以移动到gateway网关, 今天我就来说一下怎么校验请求携带的token。

首先我们需要编写一个局部过滤器

继承AbstractGatewayFilterFactory如下

gateway网关接口请求的校验方式

然后在apply方法中实现自己要校验的逻辑,所有的请求参数或者token都可以通过request获取。 验证失败我在catch捕捉异常, 然后将异常信息返回给前端请求,

返回信息的异常处理是这样的

e.printStackTrace();
ServerHttpResponse response = exchange.getResponse();
JSONObject message = new JSONObject();
message.put("status", -1);
message.put("data", e.getMessage());
byte[] bits = message.toJSONString().getBytes(StandardCharsets.UTF_8);
DataBuffer buffer = response.bufferFactory().wrap(bits);
response.setStatusCode(HttpStatus.UNAUTHORIZED);
//指定编码,否则在浏览器中会中文乱码
response.getHeaders().add("Content-Type", "text/plain;charset=UTF-8");
return response.writeWith(Mono.just(buffer));

状态码根据需求自己定义,异常信息就可以放回给前端请求。

至于校验通过,就直接放行即可

chain.filter(exchange);

如果还需要修改返回的路径请求可以这样

// 修改路径
            String newPath ="/test/" + request.getPath();
            ServerHttpRequest newRequest = request.mutate()
                    .path(newPath)
                    .build();
            exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, newRequest.getURI());
            //放行
            return chain.filter(exchange.mutate()
                    .request(newRequest).build());

以上就是过滤器的编写, 然后就是路由的配置,我是在yml文件中配置

routes:
        - id: token_routh
          uri: lb://test-service
          order: 0
          predicates:
            - Path=/test/**
          filters:
            # 去除/test
            - StripPrefix=1
            - TokenApi

filters下面的TokenApi对应的过滤器就是刚刚编写的过滤器,以上就完成了 对指定接口校验的功能。

spring cloud gateway网关请求处理过程

一、网关请求处理过程

  gateway网关接口请求的校验方式

gateway网关接口请求的校验方式

客户端向Spring Cloud Gateway发出请求。如果网关处理程序映射确定请求与路由匹配,则将其发送到网关Web处理程序。此处理程序运行通过特定于请求的过滤器链发送请求。滤波器被虚线划分的原因是滤波器可以在发送代理请求之前或之后执行逻辑。执行所有“pre”过滤器逻辑,然后进行代理请求。在发出代理请求之后,执行“post”过滤器逻辑。

在没有端口的路由中定义的URI将分别为HTTP和HTTPS URI获取默认端口设置为80和443。

  • DispatcherHandler:所有请求的调度器,负载请求分发
  • RoutePredicateHandlerMapping:路由谓语匹配器,用于路由的查找,以及找到路由后返回对应的WebHandler,DispatcherHandler会依次遍历HandlerMapping集合进行处理
  • FilteringWebHandler: 使用Filter链表处理请求的WebHandler,RoutePredicateHandlerMapping找到路由后返回对应的FilteringWebHandler对请求进行处理,FilteringWebHandler负责组装Filter链表并调用链表处理请求。

1.1、DispatcherHandler分发处理

在import org.springframework.web.reactive.DispatcherHandler;类中核心方法handle

public Mono<Void> handle(ServerWebExchange exchange) {
        if (logger.isDebugEnabled()) {
            ServerHttpRequest request = exchange.getRequest();
            logger.debug("Processing " + request.getMethodValue() + " request for [" + request.getURI() + "]");
        }
        //校验handlerMapping集合是否为空
        //依次遍历handlerMapping集合进行请求处理
        //通过mapping获取mapping对应的handler,调用handler处理
        return this.handlerMappings == null ? Mono.error(HANDLER_NOT_FOUND_EXCEPTION) : Flux.fromIterable(this.handlerMappings).concatMap((mapping) -> {
            return mapping.getHandler(exchange);
        }).next().switchIfEmpty(Mono.error(HANDLER_NOT_FOUND_EXCEPTION)).flatMap((handler) -> {
            return this.invokeHandler(exchange, handler);
        }).flatMap((result) -> {
            return this.handleResult(exchange, result);
        });
    }

DispatcherHandler的handler执行顺序

  • 校验handlerMapping
  • 遍历Mapping获取mapping对应的handler(此处会找到gateway对应的 RoutePredicateHandlerMapping,并通过 RoutePredicateHandlerMapping获取handler(FilteringWebHandler))
  • 通过handler对应的HandlerAdapter对handler进行调用(gateway使用的 SimpleHandlerAdapter) 即 FilteringWebHandler与SimpleHandlerAdapter对应

gateway网关接口请求的校验方式

其中SimpleHandlerAdapter中handler实现

public Mono<HandlerResult> handle(ServerWebExchange exchange, Object handler) {
        WebHandler webHandler = (WebHandler)handler;
        Mono<Void> mono = webHandler.handle(exchange);
        return mono.then(Mono.empty());
    }

1.2、RoutePredicateHandlerMapping 路由谓词处理映射

public class RoutePredicateHandlerMapping extends AbstractHandlerMapping {
    private final FilteringWebHandler webHandler;
    private final RouteLocator routeLocator;
    public RoutePredicateHandlerMapping(FilteringWebHandler webHandler, RouteLocator routeLocator, GlobalCorsProperties globalCorsProperties) {
        this.webHandler = webHandler;
        this.routeLocator = routeLocator;
        //设置排序字段1,此处的目的是Spring Cloud Gateway 的 GatewayWebfluxEndpoint 提供 HTTP API ,不需要经过网关
        //它通过 RequestMappingHandlerMapping 进行请求匹配处理。RequestMappingHandlerMapping 的 order = 0 ,
        // 需要排在 RoutePredicateHandlerMapping 前面。所有,RoutePredicateHandlerMapping 设置 order = 1 。
        this.setOrder(1);
        this.setCorsConfigurations(globalCorsProperties.getCorsConfigurations());
    }
    protected Mono<?> getHandlerInternal(ServerWebExchange exchange) {
        //设置mapping到上下文环境
        exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_HANDLER_MAPPER_ATTR, this.getClass().getSimpleName());
        // 查找路由
        return this.lookupRoute(exchange).flatMap((r) -> {
            exchange.getAttributes().remove(ServerWebExchangeUtils.GATEWAY_PREDICATE_ROUTE_ATTR);
            if (this.logger.isDebugEnabled()) {
                this.logger.debug("Mapping [" + this.getExchangeDesc(exchange) + "] to " + r);
            }
            //将找到的路由信息设置到上下文环境中
            exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR, r);
            //返回mapping对应的WebHandler即FilteringWebHandler
            return Mono.just(this.webHandler);
        }).switchIfEmpty(Mono.empty().then(Mono.fromRunnable(() -> {
            //当前未找到路由时返回空,并移除GATEWAY_PREDICATE_ROUTE_ATTR
            exchange.getAttributes().remove(ServerWebExchangeUtils.GATEWAY_PREDICATE_ROUTE_ATTR);
            if (this.logger.isTraceEnabled()) {
                this.logger.trace("No RouteDefinition found for [" + this.getExchangeDesc(exchange) + "]");
            }
        })));
    }
    protected CorsConfiguration getCorsConfiguration(Object handler, ServerWebExchange exchange) {
        return super.getCorsConfiguration(handler, exchange);
    }
    private String getExchangeDesc(ServerWebExchange exchange) {
        StringBuilder out = new StringBuilder();
        out.append("Exchange: ");
        out.append(exchange.getRequest().getMethod());
        out.append(" ");
        out.append(exchange.getRequest().getURI());
        return out.toString();
    }
    protected Mono<Route> lookupRoute(ServerWebExchange exchange) {
        //通过路由定位器获取路由信息
        return this.routeLocator.getRoutes().concatMap((route) -> {
            return Mono.just(route).filterWhen((r) -> {
                exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_PREDICATE_ROUTE_ATTR, r.getId());
                return (Publisher)r.getPredicate().apply(exchange);
            }).doOnError((e) -> {
                this.logger.error("Error applying predicate for route: " + route.getId(), e);
            }).onErrorResume((e) -> {
                return Mono.empty();
            });
        }).next().map((route) -> {
            if (this.logger.isDebugEnabled()) {
                this.logger.debug("Route matched: " + route.getId());
            }
            this.validateRoute(route, exchange);
            return route;
        });
    }
    protected void validateRoute(Route route, ServerWebExchange exchange) {
    }
}

RoutePredicateHandlerMapping的执行顺序

  • 通过路由定位器获取全部路由(RouteLocator)
  • 通过路由的谓语(Predicate)过滤掉不可用的路由信息
  • 查找到路由信息后将路由信息设置当上下文环境中(GATEWAY_ROUTE_ATTR)
  • 返回gatway自定的webhandler(FilteringWebHandler)

备注:

在构建方法中看到setOrder(1);作用:Spring Cloud Gateway 的 GatewayWebfluxEndpoint 提供 HTTP API ,不需要经过网关。

通过 RequestMappingHandlerMapping 进行请求匹配处理。RequestMappingHandlerMapping 的 order = 0 ,需要排在 RoutePredicateHandlerMapping 前面,所以设置 order = 1 。

1.3、FilteringWebHandler 过滤web请求处理

/**
 * 通过过滤器处理web请求的处理器
 */
public class FilteringWebHandler implements WebHandler {
    protected static final Log logger = LogFactory.getLog(FilteringWebHandler.class);
    /**
     * 全局过滤器
     */
    private final List<GatewayFilter> globalFilters;
    public FilteringWebHandler(List<GlobalFilter> globalFilters) {
        this.globalFilters = loadFilters(globalFilters);
    }
    /**
     * 包装加载全局的过滤器,将全局过滤器包装成GatewayFilter
     */
    private static List<GatewayFilter> loadFilters(List<GlobalFilter> filters) {
        return filters.stream()
                .map(filter -> {
                    //将所有的全局过滤器包装成网关过滤器
                    GatewayFilterAdapter gatewayFilter = new GatewayFilterAdapter(filter);
                    //判断全局过滤器是否实现了可排序接口
                    if (filter instanceof Ordered) {
                        int order = ((Ordered) filter).getOrder();
                        //包装成可排序的网关过滤器
                        return new OrderedGatewayFilter(gatewayFilter, order);
                    }
                    return gatewayFilter;
                }).collect(Collectors.toList());
    }
    @Override
    public Mono<Void> handle(ServerWebExchange exchange) {
        //获取请求上下文设置的路由实例
        Route route = exchange.getRequiredAttribute(GATEWAY_ROUTE_ATTR);
        //获取路由定义下的网关过滤器集合
        List<GatewayFilter> gatewayFilters = route.getFilters();
        //组合全局的过滤器与路由配置的过滤器
        List<GatewayFilter> combined = new ArrayList<>(this.globalFilters);
        //添加路由配置过滤器到集合尾部
        combined.addAll(gatewayFilters);
        //对过滤器进行排序
        AnnotationAwareOrderComparator.sort(combined);
        logger.debug("Sorted gatewayFilterFactories: "+ combined);
        //创建过滤器链表对其进行链式调用
        return new DefaultGatewayFilterChain(combined).filter(exchange);
    }
}

FilteringWebHandler的执行顺序

  • 构建一个包含全局过滤器的集合(combined)
  • 获取上下中的路由信息GATEWAY_ROUTE_ATTR
  • 将路由里的过滤器添加到集合中(combined)
  • 对过滤器集合进行排序操作
  • 通过过滤器集合组装过滤器链表,并进行调用(DefaultGatewayFilterChain与Servlet中的FilterChain与原理是一致的)
  • 通过过滤器来处理请求到具体业务服务

以上为个人经验,希望能给大家一个参考,也希望大家多多支持三水点靠木。

Java/Android 相关文章推荐
mybatis 获取无数据的字段不显示的问题
Jul 15 Java/Android
SpringBoot+VUE实现数据表格的实战
Aug 02 Java/Android
SpringBoot整合Mybatis Generator自动生成代码
Aug 23 Java/Android
spring cloud 配置中心客户端启动遇到的问题
Sep 25 Java/Android
MybatisPlus EntityWrapper如何自定义SQL
Mar 22 Java/Android
Java 超详细讲解十大排序算法面试无忧
Apr 08 Java/Android
零基础学java之带返回值的方法的定义和调用
Apr 10 Java/Android
零基础学java之带参数以及返回值的方法
Apr 10 Java/Android
Elasticsearch 配置详解
Apr 19 Java/Android
Android实现图片九宫格
Jun 28 Java/Android
Java使用HttpClient实现文件下载
Aug 14 Java/Android
Mybatis 一级缓存和二级缓存原理区别
Sep 23 Java/Android
spring cloud gateway中如何读取请求参数
Spring Cloud Gateway去掉url前缀
Jul 15 #Java/Android
小程序与后端Java接口交互实现HelloWorld入门
Java生成读取条形码和二维码的简单示例
Jul 09 #Java/Android
详细了解java监听器和过滤器
Jul 09 #Java/Android
Java使用jmeter进行压力测试
java解析XML详解
You might like
DC《神奇女侠2》因疫情推迟上映 温子仁新恐怖片《恶性》撤档
2020/04/09 欧美动漫
PHP数组对比函数,存在交集则返回真,否则返回假
2011/02/03 PHP
php中隐形字符65279(utf-8的BOM头)问题
2014/08/16 PHP
PHP整合PayPal支付
2015/06/11 PHP
Joomla语言翻译类Jtext用法分析
2016/05/05 PHP
PHP strip_tags() 去字符串中的 HTML、XML 以及 PHP 标签的函数
2016/05/22 PHP
PHP封装函数实现生成随机的字符串验证码
2017/01/24 PHP
用JavaScript获取DOM元素位置和尺寸大小的方法
2013/04/12 Javascript
JavaScript中对象property的读取和写入方法介绍
2014/12/30 Javascript
JavaScript弹出新窗口后向父窗口输出内容的方法
2015/04/06 Javascript
JQuery菜单效果的两个实例讲解(3)
2015/09/17 Javascript
实例详解angularjs和ajax的结合使用
2015/10/22 Javascript
jQuery+css实现的时钟效果(兼容各浏览器)
2016/01/27 Javascript
JavaScript自定义函数实现查找两个字符串最长公共子串的方法
2016/11/24 Javascript
angularjs实现天气预报功能
2020/06/16 Javascript
vue 插值 v-once,v-text, v-html详解
2018/01/19 Javascript
微信小程序template模版的使用方法
2019/04/13 Javascript
自定义Vue组件打包、发布到npm及使用教程
2019/05/22 Javascript
JavaScript实现串行请求的示例代码
2020/09/14 Javascript
[10:34]DOTA2上海特级锦标赛全纪录
2016/03/25 DOTA
Python下的Softmax回归函数的实现方法(推荐)
2017/01/26 Python
python基础练习之几个简单的游戏
2017/11/10 Python
Python文本特征抽取与向量化算法学习
2017/12/22 Python
用Python徒手撸一个股票回测框架搭建【推荐】
2019/08/05 Python
python新式类和经典类的区别实例分析
2020/03/23 Python
python实现梯度下降和逻辑回归
2020/03/24 Python
html5 Canvas画图教程(3)—canvas出现1像素线条模糊不清的原因
2013/01/09 HTML / CSS
使用html2canvas.js实现页面截图并显示或上传的示例代码
2018/12/18 HTML / CSS
法拉利英国精品店:Ferraris Boutique UK
2019/07/20 全球购物
大学毕业自我鉴定范文
2014/02/03 职场文书
拉拉队口号
2014/06/16 职场文书
计算机科学与技术专业求职信
2014/09/03 职场文书
租房协议书
2014/09/12 职场文书
校长四风对照检查材料
2014/09/27 职场文书
运动会新闻稿
2015/07/17 职场文书
最新动漫情报:2022年7月新番定档超过30部, OVERLORD骨王第四季也在其中噢
2022/05/04 日漫