一、Spring Cloud Gateway内部流程源码分析
(1)根据自动装配spring-cloud-gateway-core.jar的spring.factories;
(2)GatewayClassPathWarningAutoConfiguration检查前端控制器;
(3)网关自动配置GatewayAutoConfiguration;
(4)RoutePredicateHandlerMapping.getHandlerInternal(…)获取Route;
(5)执行FilteringWebHandler
二、Spring Cloud Gateway自定义谓词
Spring Cloud Gateway内置了一系列的路由谓词工厂,但是如果这些内置的路由谓词工厂不能满足业务需求的话,可以自定义路由谓词工厂来实现特定的需求;
下面列举两个例子:
1、要求请求必须携带一个token,并且token值等于指定的值,才能访问;
2、要求某个服务的用户只允许在23:00 - 6:00这个时间段内才可以访问;
1、自定义谓词具体步骤:
- (1)首先定义一个配置类,用于承载配置参数;
- (2)定义一个路由谓词工厂;
- 注:TokenRoutePredicateFactory类,前面的Token与.yml配置文件里面配置的名字对应,后面的RoutePredicateFactory名字是固定的,不能随便写,这是Spring Cloud Gateway的约定,类名须为“谓词工厂名(比如:Token)” + RoutePredicateFactory
- (3)在配置文件中启用该路由谓词工厂,即配置一个Token=123456;
时间格式不是随便配置,而是Spring Cloud Gateway的默认时间格式,采用JDK8里面的格式化:
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT);
String nowTime = dateTimeFormatter.format(ZonedDateTime.now());
System.out.println(nowTime);
到此为止就实现了一个自定义路由谓词工厂,若此时token值不相等,不在允许的访问时间段内,访问就会报404;
2、Spring Cloud Gateway谓词不匹配404处理
处理的顶层接口是WebExceptionHandler
默认实现是DefaultErrorWebExceptionHandler
我们需要覆盖它的默认实现DefaultErrorWebExceptionHandler,覆盖里面的方法,在方法里面编写我们想要返回的结果,参看我们git代码;
三 、Spring Cloud Gateway自定义路由过滤器
网关过滤器的顶层接口是GatewayFilterFactory
通常情况下可以继承AbstractGatewayFilterFactory实现自定义网关过滤器;
或者继承AbstractNameValueGatewayFilterFactory,该方式配置方式更简单,然后覆盖里面的一个方法,具体参考一下我们的样例代码,实际上理解流程思路即可,真正需要做的时候,查一下就可以;
1、Spring Cloud Gateway全局过滤器
上面的过滤器工厂是执行在指定路由之上,可以称为路由过滤器(或者局部过滤器),而全局过滤器是作用于所有的路由上,对所有的路由进行过滤;
全局过滤器的顶层接口是GlobalFilter ,和GatewayFilter 有一样的接口定义,只不过GlobalFilter 会作用于所有路由;
全局过滤器有执行顺序问题,通过getOrder()方法的返回值决定执行顺序,数值越小越靠前执行;
Spring cloud gateway默认内置了很多全局过滤器,比如:
- Combined Global Filter and GatewayFilter Ordering
- Forward Routing Filter
- The LoadBalancerClient Filter
- The ReactiveLoadBalancerClientFilter
- The Netty Routing Filter
- The Netty Write Response Filter
- The RouteToRequestUrl Filter
- The Websocket Routing Filter
- The Gateway Metrics Filter
- Marking An Exchange As Routed
2、自定义全局过滤器示例
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
/**
* 自定义全局Filter
*
*/
@Slf4j
@Component
public class CustomGlobalFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
log.info("全局Filter请求......");
MultiValueMap<String, String> valueMap = exchange.getRequest().getQueryParams();
valueMap.forEach((k, v) -> {
log.info("全局Filter拦截参数 {} ", k);
v.forEach(s -> {
log.info("全局Filter拦截参数值 = {} ", s);
});
});
return chain.filter(exchange);
}
@Override
public int getOrder() {
return 0;
}
}
四 、Spring Cloud Gateway集成ribbon负载均衡
实现原理是在全局LoadBalancerClientFilter中进行拦截,然后再该过滤器中依赖LoadBalancerClient loadBalancer,而此负载均衡接口的具体实现是RibbonLoadBalancerClient,即spring cloud gateway已经整合好了ribbon,已经可以实现负载均衡,我们不需要做任何工作,网关对后端微服务的转发就已经具有负载均衡功能;
五 、Spring cloud gateway跨域CORS
我们知道,传统的Ajax请求只能获取在同一个域名下的资源,但是HTML5规范中打破了这种限制,允许Ajax发起跨域的请求;(只是需要设置一下)
其实浏览器本身是可以发起跨域请求的,比如你可以链接一个另一个域名下的图片或者js,比如
,但是javascript脚本是不能获取这些另一个域名下的资源内容的;
CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing),它允许浏览器向跨域的另一台服务器发出XMLHttpRequest请求,从而克服了AJAX只能访问同域名下的资源的限制;
这种CORS使用了一个额外的HTTP响应头来赋予当前user-agent(浏览器)获得跨域资源的权限,这里的跨域也就是Cross-Origin的概念,这里的权限就是访问另一个域名下的资源权限;
CORS是现在HTML5标准中的一部分,在大部分现代浏览器中都有所支持,可能在某些老版本的浏览器不支持CORS,如果要兼容一些老的浏览器版本,则需要采用JSONP进行跨域请求;
同源与非同源的定义(跨域和不跨域)
如果 访问协议、端口(如果指定了端口的话)、host都相同,则称之为同源(不跨域),否则为非同源(跨域);
比如源链接: http:///dir/page.html
Spring Cloud Gateway解决跨域问题,只需要配置如下代码即可:
/**
* 配置网关跨域cors请求支持
*/
@Configuration
public class CorsConfig {
@Bean
public CorsWebFilter corsFilter() {
CorsConfiguration config = new CorsConfiguration();
config.addAllowedMethod("*");
config.addAllowedOrigin("*");
config.addAllowedHeader("*");
UrlBasedCorsConfigurationSource source = new
UrlBasedCorsConfigurationSource(new PathPatternParser());
source.registerCorsConfiguration("/**", config);
return new CorsWebFilter(source);
}
}