1. gateway过滤器

Gateway中Filter的生命周期只有两个:pre 和 post:
PRE: 这种过滤器在请求被路由之前调用,可实现身份验证。
POST:这种过滤器在路由到微服务以后执行,可用来为响应添加标准的HTTP Header、收集统计信息和指标、将响应从微服务发送给客户端等。

Gateway 的Filter从作用范围可分为两种: GatewayFilter与GlobalFilter。 GatewayFilter:应用到单个路由或者一个分组的路由上,GlobalFilter:应用到所有的路由上。

内置过滤器:

过滤器工厂

作用

使用

AddRequestHeader

为原始请求添加Header

- AddRequestHeader=X-Request-red, blue

AddRequestParameter

为原始请求添加请求参数

- AddRequestParameter=color, blue

AddResponseHeader

为原始响应添加Header

- AddResponseHeader=X-Response-Red, Blue

DedupeResponseHeader

剔除响应头中重复的值

- DedupeResponseHeader=Access-Control-Allow-Credentials Access-Control-Allow-Origin

CircuitBreaker

spring cloud gateway 熔断器,支持resilience4j

java gateway防止header头透传_自定义

FallbackHeaders

为fallbackUri的请求头中添加具 体的异常信息

java gateway防止header头透传_限流_02

MapRequestHeader

从已有的请求头中获取值,并赋值给一个新的请求头

- MapRequestHeader=Blue, X-Request-Red

PrefixPath

为原始请求路径添加前缀

- PrefixPath=/mypath

PreserveHostHeader

为请求添加一个 preserveHostHeader=true的属 性,路由过滤器会检查该属性以 决定是否要发送原始的Host

- PreserveHostHeader

RequestRateLimiter

用于对请求限流,限流算法为令牌桶

RedirectTo

将原始请求重定向到指定的URL

RemoveHopByHopHeadersFilter

删除header

- RemoveResponseHeader=X-Response-Foo

RemoveRequestParameter

删除参数

- RemoveRequestParameter=red

RequestHeaderSize

限制请求头大小,超过限制大小将会返回431

- RequestHeaderSize=1000B

RewritePath

重写原始的请求路径

- RewritePath=/red/?(?.*), /${segment}

RewriteResponseHeader

重写原始响应中的某个Header

SaveSession

在转发请求之前,强制执行

- SaveSession

SecureHeaders

为原始响应添加一系列起安全作用的响应头

spring.cloud.gateway.filter.secure-headers.disable=x-frame-options,strict-transport-security

SetPath

修改原始的请求路径

SetResponseHeader

修改原始响应中某个Header的值

- SetRequestHeader=X-Request-Red, Blue

SetRequestHeader

修改请求头的值

- SetRequestHeader=X-Request-Red, Blue

SetStatus

修改原始响应的状态码

- SetStatus=401

StripPrefix

用于截断原始请求的路径,使用数字表示要截断的路径的数量

- StripPrefix=2

Retry

针对不同的响应进行重试

RequestSize

设置允许接收最大请求包的大小。如果请求包大小超过设置的值,则返回413,请求包大小,单位为字 节,默认值为5M

SetRequestHostHeader

设置请求host

ModifyRequestBody

在转发请求之前修改原始请求体

内容

ModifyResponseBody

修改响应内容

2 自定义过滤器

自定义一个过滤器打印请求日志
在配置文件中配置:

# true/false控制日志是否开启
- MyLog=true

自定义一个过滤器工厂:

/**
 * 自定义过滤器
 */
@Component
public class MyLogGatewayFilterFactory extends AbstractGatewayFilterFactory<MyLogGatewayFilterFactory.MyLogConfig> {


    public MyLogGatewayFilterFactory() {
        super(MyLogConfig.class);
    }

    /**
     * 读取配置文件中的参数 赋值到 配置类中
     * @return
     */
    @Override
    public List<String> shortcutFieldOrder() {
        return Arrays.asList("showLog");
    }

    @Override
    public GatewayFilter apply(MyLogConfig config) {
        return new GatewayFilter() {
            @Override
            public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
                if (config.showLog) {
                    System.out.println("请求路径:" + exchange.getRequest().getPath());
                }
                return chain.filter(exchange);
            }
        };
    }

    /**
     * 配置类接收配置的参数
     */
    public static class MyLogConfig {
        private boolean showLog;

        public boolean isShowLog() {
            return showLog;
        }

        public void setShowLog(boolean showLog) {
            this.showLog = showLog;
        }
    }
}

然后启动测试:

java gateway防止header头透传_spring_03

3 自定义全局过滤器

全局过滤器作用于所有路由, 无需配置。通过全局过滤器可以实现对权限的统一校验,安全性验证等功能。开发一个全局过滤器实现接口的统一鉴权,大概逻辑为:当客户端第一次请求服务时,服务端对用户进行信息认证(登录) ,如果认证通过,将用户信息进行加密形成token,返回给客户端,作为登录凭证; 以后每次请求,客户端都携带认证的token 服务端对token进行解密,判断是否有效。自定义全局过滤器需要实现GlobalFilter和Ordered接口。

@Component
public class AuthGlobalFilter implements GlobalFilter, Ordered {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String token = exchange.getRequest().getHeaders().getFirst("token");
        if (StringUtils.hasText(token)) {
            // TODO 处理鉴权逻辑
            System.out.println("auth: 鉴权成功");
            return chain.filter(exchange);
        } else {
            System.out.println("auth: token丢失!!");
            exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
            return exchange.getResponse().setComplete();
        }
    }

    /**
     * 数值越小越先执行
     * @return
     */
    @Override
    public int getOrder() {
        return 0;
    }
}

4 网关限流

网关是所有请求的公共入口,所以可以在网关进行限流,而且限流的方式也很多。Sentinel支持对SpringCloud Gateway、Zuul等主流网关进 行限流。从1.6.0版本开始,Sentinel提供了SpringCloud Gateway的适配模块,可以提供两种资源维度的限流: route维度,即在Spring配置文件中配置的路由条目,资源名为对应的routeId;自定义API维度,用户可以利用Sentinel提供的API来自定义一些API分组。

使用sentinel实现网关限流需要先引入依赖:

<dependency>
    <groupId>com.alibaba.csp</groupId>
     <artifactId>sentinel-spring-cloud-gateway-adapter</artifactId>
</dependency>

限流配置:

package com.tdt.platformcloud.gateway.config;

import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayRuleManager;
import com.alibaba.csp.sentinel.adapter.gateway.sc.SentinelGatewayFilter;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.BlockRequestHandler;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.GatewayCallbackManager;
import com.alibaba.csp.sentinel.adapter.gateway.sc.exception.SentinelGatewayBlockExceptionHandler;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.web.reactive.function.BodyInserter;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.web.reactive.result.view.ViewResolver;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import javax.annotation.PostConstruct;
import java.util.*;

/**
 * 请求限流配置
 */
@Configuration
public class RequestLimitConfig {

    private final List<ViewResolver> viewResolvers;
    private final ServerCodecConfigurer serverCodecConfigurer;

    public RequestLimitConfig(ObjectProvider<List<ViewResolver>> viewResolversProvider, ServerCodecConfigurer serverCodecConfigurer) {
        this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList);
        this.serverCodecConfigurer = serverCodecConfigurer;
    }

    /**
     * 初始化限流过滤器
     * @return
     */
    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public GlobalFilter sentinelGatewayFilter() {
        return new SentinelGatewayFilter();
    }

    /**
     * 配置初始化的限流参数
     */
    @PostConstruct
    public void initGatewayRules() {
        Set<GatewayFlowRule> rules = new HashSet<>();
        rules.add(new GatewayFlowRule("product_route") // 资源名称,对应路由ID
                .setCount(1)  // 阈值
                .setIntervalSec(1)); // 统计时间窗口,单位是秒,默认1s
        GatewayRuleManager.loadRules(rules);
    }

    /**
     * 限流异常处理器
     * @return
     */
    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() {
        return new SentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);
    }

    /**
     * 自定义限流异常页面
     */
    @PostConstruct
    public void initBlockHandlers() {
        BlockRequestHandler blockRequestHandler = new BlockRequestHandler() {
            @Override
            public Mono<ServerResponse> handleRequest(ServerWebExchange serverWebExchange, Throwable throwable) {
                Map<String, Object> result = new HashMap<>();
                result.put("code", 0);
                result.put("message", "接口被限流了,稍后再试");
                return ServerResponse.status(HttpStatus.OK)
                        .contentType(MediaType.APPLICATION_JSON)
                        .body(BodyInserters.fromValue(result));
            }
        };
        GatewayCallbackManager.setBlockHandler(blockRequestHandler);
    }
}

然后测试一下:

java gateway防止header头透传_spring_04


还可以自定义api分组进行更细粒度的限流。