个人主页:道友老李
欢迎加入社区:道友老李的学习社区
Spring Cloud Alibaba是阿里巴巴提供的一站式微服务解决方案,是Spring Cloud体系中的一个重要分支,它将阿里巴巴在微服务领域的实践经验和开源技术进行了整合,为开发者提供了一系列便捷的工具和组件,用于构建分布式微服务应用。以下是其详细介绍:
SpringCloudGateway 内置了很多的过滤器工厂,我们通过一些过滤器工厂可以进行一些业务逻辑处理器,比如添加剔除响应头,添加去除参数等
官网:https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#gatewayfilter-factories
spring:
cloud:
gateway:
routes:
- id: add_request_header_route
uri: https://example.org
filters:
- AddRequestHeader=X-Request-Foo, Bar
为原始请求添加名为 X-Request-Foo
,值为 Bar
的请求头。
spring:
cloud:
gateway:
routes:
- id: add_request_parameter_route
uri: https://example.org
filters:
- AddRequestParameter=foo, bar
为原始请求添加请求参数 foo=bar
spring:
cloud:
gateway:
routes:
- id: add_response_header_route
uri: https://example.org
filters:
- AddResponseHeader=X-Response-Foo, Bar
添加名为 X-Request-Foo
,值为 Bar
的响应头。
Spring Cloud Greenwich SR2提供的新特性,低于这个版本无法使用。
强烈建议阅读一下类org.springframework.cloud.gateway.filter.factory.DedupeResponseHeaderGatewayFilterFactory上的注释,比官方文档写得还好。
spring:
cloud:
gateway:
routes:
- id: dedupe_response_header_route
uri: https://example.org
filters:
- DedupeResponseHeader=Access-Control-Allow-Credentials Access-Control-Allow-Origin, RETAIN_FIRST
剔除重复的响应头。
举个例子:
我们在Gateway以及微服务上都设置了CORS(解决跨域)header,如果不做任何配置,请求 -> 网关 -> 微服务,获得的响应就是这样的:
Access-Control-Allow-Credentials: true, true
Access-Control-Allow-Origin: https://www.dylledu.com, https://www.dylledu.com
也就是Header重复了。要想把这两个Header去重,只需设置成如下即可。
filters:
- DedupeResponseHeader=Access-Control-Allow-Credentials Access-Control-Allow-Origin
也就是说,想要去重的Header如果有多个,用空格分隔即可;
去重策略:
RETAIN_FIRST: 默认值,保留第一个值
RETAIN_LAST: 保留最后一个值
RETAIN_UNIQUE: 保留所有唯一值,以它们第一次出现的顺序保留
spring:
cloud:
gateway:
routes:
- id: prefixpath_route
uri: https://example.org
filters:
- PrefixPath=/mypath
为匹配的路由添加前缀。例如:访问 ${GATEWAY_URL}/hello
会转发到 https://example.org/mypath/hello
spring:
cloud:
gateway:
routes:
- id: preserve_host_route
uri: https://example.org
filters:
- PreserveHostHeader
如果不设置,那么名为 Host
的Header由Http Client控制;如果设置了,那么会设置一个请求属性(preserveHostHeader=true),路由过滤器会检查从而去判断是否要发送原始的、名为Host的Header。
spring:
cloud:
gateway:
routes:
- id: requestratelimiter_route
uri: https://example.org
filters:
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 10
redis-rate-limiter.burstCapacity: 20
spring:
cloud:
gateway:
routes:
- id: prefixpath_route
uri: https://example.org
filters:
# 配置成HTTP状态码, URL的形式
- RedirectTo=302, http://www.dyll.com
Location
的Header。上面配置表达的意思是: ${GATEWAY_URL}/hello
会重定向到 https://ecme.org/hello
,并且携带一个 Location:http://www.dyll.com
的Header。
spring.cloud.gateway.filter.remove-hop-by-hop.headers: Connection,Keep-Alive
移除转发请求的Header,多个用 ,
分隔。默认情况下,移除如下Header。这些Header是由 IETF 组织规定的。
spring:
cloud:
gateway:
routes:
- id: removerequestheader_route
uri: https://example.org
filters:
- RemoveRequestHeader=X-Request-Foo
原始请求删除名为 X-Request-Foo
的请求头。
spring:
cloud:
gateway:
routes:
- id: removeresponseheader_route
uri: https://example.org
filters:
- RemoveResponseHeader=X-Response-Foo
删除名为 X-Request-Foo
的响应头。
spring:
cloud:
gateway:
routes:
- id: rewritepath_route
uri: https://example.org
predicates:
- Path=/foo/**
filters:
# 配置成原始路径正则, 重写后的路径的正则
- RewritePath=/foo/(?>.*), /$\{segment}
重写请求路径。如上配置,访问 /foo/bar
会将路径改为 /bar
再转发,也就是会转发到 https://example.org/bar
。需要注意的是,由于YAML语法,需用 $\
替换 $$
。
spring:
cloud:
gateway:
routes:
- id: rewriteresponseheader_route
uri: https://example.org
filters:
- RewriteResponseHeader=X-Response-Foo, password=[^&]+, password=***
如果名为 X-Response-Foo
的响应头的内容是 /42?user=ford&password=omg!what&flag=true
,则会被修改为 /42?user=ford&password=***&flag=true
。
spring:
cloud:
gateway:
routes:
- id: save_session
uri: https://example.org
predicates:
- Path=/foo/**
filters:
- SaveSession
在转发到后端微服务请求之前,强制执行 WebSession::save
操作。用在那种像 Spring Session
延迟数据存储(笔者注:数据不是立刻持久化)的,并希望在请求转发前确保session状态保存情况。
如果你将 Spring Secutiry
于 Spring Session
集成使用,并想确保安全信息都传到下游机器,你就需要配置这个filter。
添加一系列起安全作用的响应头。Spring Cloud Gateway参考了这篇博客的建议:https://blog.appcanary.com/2017/http-security-headers.html
默认会添加如下Header(包括值):
X-Xss-Protection:1; mode=block
Strict-Transport-Security:max-age=631138519
X-Frame-Options:DENY
X-Content-Type-Options:nosniff
Referrer-Policy:no-referrer
Content-Security-Policy:default-src 'self' https:; font-src 'self' https: data:; img-src 'self' https: data:; object-src 'none'; script-src https:; style-src 'self' https: 'unsafe-inline'
X-Download-Options:noopen
X-Permitted-Cross-Domain-Policies:none
如果你想修改这些Header的值,可使用如下配置:
前缀:spring.cloud.gateway.filter.secure-headers
上面的header对应的后缀:
xss-protection-header
strict-transport-security
frame-options
content-type-options
referrer-policy
content-security-policy
download-options
permitted-cross-domain-policies
例如:spring.cloud.gateway.filter.secure-headers.xss-protection-header: 你想要的值
如果想禁用某些Header,可使用如下配置:spring.cloud.gateway.filter.secure-headers.disable
,多个用 ,
分隔。例如:spring.cloud.gateway.filter.secure-headers.disable=frame-options,download-options
。
spring:
cloud:
gateway:
routes:
- id: setpath_route
uri: https://example.org
predicates:
- Path=/foo/{segment}
filters:
- SetPath=/{segment}
采用路径 template
参数,通过请求路径的片段的模板化,来达到操作修改路径的母的,运行多个路径片段模板化。
如上配置,访问 ${GATEWAY_PATH}/foo/bar
,则对于后端微服务的路径会修改为 /bar
。
spring:
cloud:
gateway:
routes:
- id: setresponseheader_route
uri: http://example.org
filters:
- SetResponseHeader=X-Response-Foo, Bar
如果后端服务响应带有名为 X-Response-Foo
的响应头,则将值改为替换成 Bar
。
spring:
cloud:
gateway:
routes:
- id: setstatusstring_route
uri: http://example.org
filters:
- SetStatus=BAD_REQUEST
- id: setstatusint_route
uri: http://example.org
filters:
- SetStatus=401
修改响应的状态码,值可以是数字,也可以是字符串。但一定要是Spring HttpStatus
枚举类中的值。如上配置,两种方式都可以返回HTTP状态码401。
spring:
cloud:
gateway:
routes:
- id: nameRoot
uri: http://nameservice
predicates:
- Path=/name/**
filters:
- StripPrefix=2
数字表示要截断的路径的数量。如上配置,如果请求的路径为 /name/bar/foo
,则路径会修改为 /foo
,也就是会截断2个路径。
spring:
cloud:
gateway:
routes:
- id: retry_test
uri: http://localhost:8080/flakey
predicates:
- Host=*.retry.com
filters:
- name: Retry
args:
retries: 3
statuses: BAD_GATEWAY
针对不同的响应做重试,可配置如下参数:
retries
: 重试次数statuses
: 需要重试的状态码,取值在 org.springframework.http.HttpStatus
中methods
: 需要重试的请求方法,取值在 org.springframework.http.HttpMethod
中series
: HTTP状态码系列,取值在 org.springframework.http.HttpStatus.Series
中spring:
cloud:
gateway:
routes:
- id: request_size_route
uri: http://localhost:8080/upload
predicates:
- Path=/upload
filters:
- name: RequestSize
args:
# 单位字节
maxSize: 5000000
为后端服务设置收到的最大请求包大小。如果请求大小超过设置的值,则返回 413 Payload Too Large
。默认值是5M
spring:
cloud:
gateway:
default-filters:
- AddResponseHeader=X-Response-Default-Foo, Default-Bar
- PrefixPath=/httpbin
断点打在 org.springframework.cloud.gateway.filter.NettyRoutingFilter#filter
,就可以调试Gateway转发的具体细节了
添加如下配置,可观察到一些请求细节:
logging:
level:
org.springframework.cloud.gateway: trace
org.springframework.http.server.reactive: debug
org.springframework.web.reactive: debug
reactor.ipc.netty: debug
自定义过滤器工厂-方式1
继承: AbstractGatewayFilterFactory
参考示例:org.springframework.cloud.gateway.filter.factory.RequestSizeGatewayFilterFactory
spring:
cloud:
gateway:
routes:
filters:
- name: RequestSize
args:
# 单位字节
maxSize: 5000000
自定义过滤器工厂-方式2
继承: AbstractNaeValueGatewayFilterFactory 【AbstractNaeValueGatewayFilterFactory 其实继承了AbstractGatewayFilterFactory,所以AbstractNaeValueGatewayFilterFactory 对AbstractGatewayFilterFactory的简化】
参考示例:org.springframework.cloud.gateway.filter.factory.AddRequestHeaderGatewayFilterFactory
spring:
cloud:
gateway:
routes:
- id: add_response_header_route
uri: https://example.org
filters:
- AddResponseHeader=X-Response-Foo, Bar
// 这里的过滤器和我们以前的过滤器还不一样,tomcat过滤器是属于HttpServletRequst\HttpServletResponse
// 我们这里底层是基于Netty的,它是将我们的请求和响应封装到我们的ServerWebExchange然后进行出里这里是对应的变化
记录日志
编写的类一定以GatewayFilterFactory结尾
代码
@Slf4j
@Component
public class PrintLogGatewayFilterFactory extends AbstractNameValueGatewayFilterFactory {
@Override
public GatewayFilter apply(NameValueConfig config) {
// 放到这里应用启动的时候会执行两次次,这是一个坑,需要放到方法里面
// log.info("打印请求信息:{}:{}",config.getName(),config.getValue());
return new GatewayFilter() {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
log.info("打印请求信息:{}:{}",config.getName(),config.getValue());
// 获取request就可以进行修改了
ServerHttpRequest modifiedRequest = exchange.getRequest().mutate().build();
// 设置请求值
// ServerHttpRequest modifiedRequest = exchange.getRequest().mutate()
// .header(config.getName(), value).build();
ServerWebExchange modifiedExchange = exchange.mutate().request(modifiedRequest).build();
return chain.filter(modifiedExchange);
}
};
}
}
配置
前面的 GatewayFilter 工厂是在某一特定路由策略中设置的,仅对这一种路由生效。若要使某些过滤效果应用到所有路由策略中,就可以将该 GatewayFilter 工厂定义在全局Filters中。修改 gateway 工程配置文件。
https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#global-filters
GlobalFilter 接口和 GatewayFilter 有一样的接口定义,只不过, GlobalFilter 会作用于所有路由。
官方声明:GlobalFilter的接口定义以及用法在未来的版本可能会发生变化。
LoadBalancerClientFilter 会查看exchange的属性 ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR 的 值(一个URI),如果该值的scheme是 lb,比如:lb://dyll-user,它将会使用Spring Cloud的LoadBalancerClient 来 将 dyll-user解析成实际的host和port,并替换掉 ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR 的内 容。
其实就是用来整合负载均衡器Ribbon的
实现GlobalFilter接口
增加Order注解,值越小越靠前
@Component
@Slf4j
@Component
public class IPAddressStatisticsFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
InetSocketAddress host = exchange.getRequest().getHeaders().getHost();
if (host == null || host.getHostName() == null) {
exchange.getResponse().setStatusCode(HttpStatus.BAD_REQUEST);
return exchange.getResponse().setComplete();
}
String hostName = host.getHostName();
AtomicInteger count = IpCache.CACHE.getOrDefault(hostName, new AtomicInteger(0));
count.incrementAndGet();
IpCache.CACHE.put(hostName, count);
log.info("IP地址:" + hostName + ",访问次数:" + count.intValue());
return chain.filter(exchange);
}
@Override
public int getOrder() {
return -1;
}
}
public class IpCache {
public static final Map<String, AtomicInteger> CACHE = new ConcurrentHashMap<>();
}