Spring Cloud Gateway 配置前置、后置过滤器

2,435 阅读1分钟

这是我参与8月更文挑战的第5天,活动详情查看:8月更文挑战

工作原理

image.png

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

测试版本号

<parent>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-parent</artifactId>
   <version>2.4.4</version>
   <relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
   <maven.compiler.source>11</maven.compiler.source>
   <maven.compiler.target>11</maven.compiler.target>
   <java.version>11</java.version>
   <spring-cloud.version>2020.0.2</spring-cloud.version>
   <r2dbc-releasetrain.version>1.3.0</r2dbc-releasetrain.version>
</properties>

效果贴图

image.png

代码实现

package com.holland.gateway.filter;

import org.reactivestreams.Publisher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.SecurityWebFiltersOrder;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.web.server.SecurityWebFilterChain;
import org.springframework.web.server.WebFilter;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.nio.charset.StandardCharsets;

@Configuration
@EnableWebFluxSecurity
public class CustomWebFilterChain {

    private final Logger logger = LoggerFactory.getLogger(CustomWebFilterChain.class);

    @Bean
    public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
        http
                .addFilterAfter(preFilter(), SecurityWebFiltersOrder.AUTHENTICATION)
                .addFilterBefore(postFilter(), SecurityWebFiltersOrder.LAST)
                .csrf(ServerHttpSecurity.CsrfSpec::disable);
        return http.build();
    }

    private WebFilter preFilter() {
        return (exchange, chain) -> {
            final ServerHttpRequest request = exchange.getRequest();
            final ServerHttpResponse originalResponse = exchange.getResponse();

            //过滤规则
            if (false) {
                originalResponse.setStatusCode(HttpStatus.UNAUTHORIZED);
                return originalResponse.setComplete();
            }
            return chain.filter(exchange);
        };
    }

    private WebFilter postFilter() {
        return (exchange, chain) -> {
            final ServerHttpRequest request = exchange.getRequest();
            logger.debug("{} {}", request.getMethod(), request.getURI());

            final ServerHttpResponse originalResponse = exchange.getResponse();
            final ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(originalResponse) {
                @Override
                public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
                    //修改header
                    final HttpHeaders httpHeaders = originalResponse.getHeaders();
                    httpHeaders.add("myHeader", "holland");
                    //输出返回结果
                    if (body instanceof Flux || body instanceof Mono) {
                        final Mono<Void> newMono = super.writeWith(
                                DataBufferUtils.join(body)
                                        .doOnNext(dataBuffer -> {
                                            String respBody = dataBuffer.toString(StandardCharsets.UTF_8);
                                            //输出body
                                            logger.debug("Response : status = {}, body = {}", exchange.getResponse().getStatusCode().toString(), respBody);
                                        })
                        );
                        return newMono;
                    }
                    return super.writeWith(body);
                }
            };
            return chain.filter(exchange.mutate().response(decoratedResponse).build());
        };
    }
}