路由route的组成部分

	 id:  路由的ID
	 uri: 匹配路由的转发地址
	 predicates:  配置该路由的断言,通过PredicateDefinition类进行接收配置。
	 order:  路由的优先级,数字越小,优先级越高。
	 Filter: 过滤器 过滤掉一些请求, 满足则转发

什么是predicates

   配置该路由的断言,通过PredicateDefinition类进行接收配置。
   Predicate 来源于Java8,接受输入参数,返回一个布尔值结果
   Spring Cloud Gateway 中 Spring 利用 Predicate 的特性实现了各种路由匹配规则
   转发的判断条件.
   SpringCloud Gateway支持多种方式,常见如:Path、Query、Method、Header等
   支持多个Predicate请求的转发是必须满足所有的Predicate后才可以进行路由转发


参数编写规则 XXXRoutePredicateFactory,使用XXX作为参数配置, 例如下面
predicates:

  • Host=
  • Path=
  • Method=
  • Header=
  • Query=
  • Cookie=

源码剖析

我们来看一个类 叫做 BeforeRoutePredicateFactory 类 这个类定义断言行为是 如果当前时间大于我们配置的时候,则不允许转发。源码如下

/*
 * Copyright 2013-2019 the original author or authors.
 *
 * 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
 *
 *      https://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.
 * 1: 继承 AbstractRoutePredicateFactory 类
 * 2: 重写 apply 方法  定义我们的转发规则
 */

package org.springframework.cloud.gateway.handler.predicate;

import java.time.ZonedDateTime;
import java.util.Collections;
import java.util.List;
import java.util.function.Predicate;

import org.springframework.web.server.ServerWebExchange;

/**
 * @author Spencer Gibb
 */
public class BeforeRoutePredicateFactory
		extends AbstractRoutePredicateFactory<BeforeRoutePredicateFactory.Config> {

	/**
	 * DateTime key.
	 */
	public static final String DATETIME_KEY = "datetime";

	public BeforeRoutePredicateFactory() {
		super(Config.class);
	}

	@Override
	public List<String> shortcutFieldOrder() {
		return Collections.singletonList(DATETIME_KEY);
	}

	@Override
	public Predicate<ServerWebExchange> apply(Config config) {
		return new GatewayPredicate() {
			@Override
			public boolean test(ServerWebExchange serverWebExchange) {
				final ZonedDateTime now = ZonedDateTime.now();
				return now.isBefore(config.getDatetime());
			}

			@Override
			public String toString() {
				return String.format("Before: %s", config.getDatetime());
			}
		};
	}

	public static class Config {

		private ZonedDateTime datetime;

		public ZonedDateTime getDatetime() {
			return datetime;
		}

		public void setDatetime(ZonedDateTime datetime) {
			this.datetime = datetime;
		}

	}

}
 则我们可以同相同的方式 自定义我们自己的转发规则。

怎么做

需求: 自定义我们的规格 配置传入参数必须要有orderId 并且orderId的值配置为10 否则禁止转发 代码如下
package com.leave.cloud.forward;


import org.springframework.cloud.gateway.handler.predicate.AbstractRoutePredicateFactory;
import org.springframework.cloud.gateway.handler.predicate.GatewayPredicate;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.springframework.web.server.ServerWebExchange;
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;

/**
 * @Description
 * @Author 自定义断言工厂
 * @Version V1.0.0
 * @Date 2021/6/16 0016
 */
@Component
public class ParameterRoutePredicateFactory extends AbstractRoutePredicateFactory<ParameterRoutePredicateFactory.Config> {
  //与内部类属性名保持一致
  private final static String parameter = "orderId";
  public ParameterRoutePredicateFactory() {
    super(Config.class);
  }
  @Override
  public List<String> shortcutFieldOrder() {
    //赋值 为 内部类 Config 中的 orderId属性赋值
    return Arrays.asList(parameter);
  }
  @Override
  public Predicate<ServerWebExchange> apply(Config config) {
    return new GatewayPredicate() {
      @Override
      public boolean test(ServerWebExchange serverWebExchange) {
        //获取请求参数体
        final String firstOrderId =serverWebExchange.getRequest().getQueryParams().getFirst(parameter);
        Assert.notNull(firstOrderId);
        return config.getOrderId().equals(firstOrderId);
      }
      @Override
      public String toString() {
        return String.format("Parameter: %s", config.getOrderId());
      }
    };
  }
  public static class Config {
    private String orderId;
    public String getOrderId() {
      return orderId;
    }
    public void setOrderId(String orderId) {
      this.orderId = orderId;
    }
  }
}

yml的配置如下

    gateway:
      routes: #数组形式
        - id: xdclass-order-service  #路由唯一标识
          uri: lb://order-service  #想要转发到的地址
          order: 1 #优先级,数字越小优先级越高
          predicates: #断言 配置参数
            - Parameter=10

请求测试截图如下
在这里插入图片描述
我们把参数换成9
在这里插入图片描述
可以看到404 访问路径不存在 没有进行转发

什么是filters

是一种过滤器,能过滤掉一些不满足要求的request请求。如下图
在这里插入图片描述

过滤器生命周期

	PRE: 这种过滤器在请求被路由之前调用,一般用于鉴权、限流等
	POST:这种过滤器在路由到微服务以后执行,一般用于修改响应结果,比如增加header信息、打点结果日志

网关过滤器分类

	局部过滤器GatewayFilter:应用在某个路由上,每个过滤器工厂都对应一个实现类,并且这些类的名称必须以 GatewayFilterFactory 结尾
	全局过滤器:作用全部路由上

内置很多局部过滤器,顶级接口 GatewayFilterFactory

在这里插入图片描述

内置很多全局过滤器,顶级接口 GlobalFilter

在这里插入图片描述

怎么做

需求1: 自定义局部过滤器, 要求订单服务的请求头中必须含有token , 代码如下

package com.leave.cloud.filters;

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.stereotype.Component;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.Arrays;
import java.util.List;

import static org.springframework.cloud.gateway.support.GatewayToStringStyler.filterToStringCreator;


/**
 * @Description
 * @Author 局部过滤器
 * @Version V1.0.0
 * @Date 2021/6/16 0016
 */
@Component
public class TokenGatewayFilterFactory extends AbstractGatewayFilterFactory<TokenGatewayFilterFactory.Config> {

  /**
   * Token_KEY
   */
  public static final String Token_KEY = "token";

  public static final String enable = "true";

  public TokenGatewayFilterFactory() {
    super(Config.class);
  }

  @Override
  public List<String> shortcutFieldOrder() {
    return Arrays.asList(Token_KEY);
  }

  @Override
  public GatewayFilter apply(Config config) {
    return new GatewayFilter() {
      @Override
      public Mono<Void> filter(ServerWebExchange exchange,
                               GatewayFilterChain chain) {
       //是否开启token验证
       if(config.getToken().equals(enable)){
          String token = exchange.getRequest().getHeaders().getFirst(Token_KEY);
          if (StringUtils.isEmpty(token)) {
            return exchange.getResponse().setComplete();
          }
       }
        //生产环境需要验证token的有效性
        return chain.filter(exchange);
      }
      @Override
      public String toString() {
        return filterToStringCreator(TokenGatewayFilterFactory.this)
            .append("token", config.getToken()).toString();
      }
    };
  }

  public static class Config {
    private String token;

    public String getToken() {
      return token;
    }

    public void setToken(String token) {
      this.token = token;
    }
  }
}

yml配置

    gateway:
      routes: #数组形式
        - id: xdclass-order-service  #路由唯一标识
          uri: lb://xdclass-order-service  #想要转发到的地址
          order: 1 #优先级,数字越小优先级越高
          predicates: #断言 配置哪个路径才转发
            - Path=/order/**
            - Parameter=10
          filters: #过滤器,请求在传递过程中通过过滤器修改
              - StripPrefix=1  #去掉第一层前缀
              - Token=true #是否开启token验证

测试调用
不带token
在这里插入图片描述
带token 在这里插入图片描述
需求2: 自定义全局过滤器, 要求必须携带token,并且token的值为userAuth , 代码如下

package com.leave.cloud.filters;

import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.Objects;

/**
 * @Description
 * @Author 全局过滤器
 * @Version V1.0.0
 * @Date 2021/6/16 0016
 */
@Component
public class AuthGlobalFilter implements GlobalFilter, Ordered {

  private final static String User_Auth = "userAuth";


  @Override
  public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
    String token = exchange.getRequest().getHeaders().getFirst("token");
    if (Objects.isNull(token)) {
      return notCheckReturn(exchange);
    } else {
      if (token.equals(User_Auth)) {
        return chain.filter(exchange);
      }
      return notCheckReturn(exchange);
    }
  }

  private Mono<Void> notCheckReturn(ServerWebExchange exchange) {
    exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
    return exchange.getResponse().setComplete();
  }

  /**
   * 执行等级
   * @return
   */
  @Override
  public int getOrder() {
    return 0;
  }
}

测试调用
token值不为userAuth 时
在这里插入图片描述
为userAuth时
在这里插入图片描述

总结

gateway最大的能力是在于转发,一些过滤最好下沉到各个服务。否则会影响性能

人有逆天之时,天无绝人之路

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐