Spring Cloud Gateway(读取、修改 Request Body)

Spring Cloud Gateway(以下简称 SCG)做为网关服务,是其他各服务对外中转站,通过 SCG 进行请求转发。

在请求到达真正的微服务之前,我们可以在这里做一些预处理,比如:来源合法性检测,权限校验,反爬虫之类…

之前是在各个微服务的拦截器里对来解密验证的,现在既然有了网关,自然而然想把这一步骤放到网关层来统一解决
 

Spring Cloud(十八):Spring Cloud Gateway(读取、修改 Request Body)

如果是使用普通的 Web 编程中(比如用 Zuul),这本就是一个 pre filter 的事儿,把之前 Interceptor 中代码搬过来稍微改改就 OK 了。

不过因为使用的 SCG,它基于 Spring 5 的 WebFlux,即 Reactor 编程,要读取 Request Body 中的请求参数就没那么容易了。

创建自定义过滤器继承AbstractGatewayFilterFactory

@Component
@Slf4j
public class ReqApiPermissionFilterFactory extends AbstractGatewayFilterFactory<ReqApiPermissionFilterFactory.Config> {

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


    static class Config {

    }

    @Value("${token.user.url}")
    String userURL;

    @Value("${middle.platform.url}")
    String middlePlatformURL;

    private static final String CONTENT_TYPE = "Content-Type";

    private static final String CONTENT_TYPE_JSON = "application/json";

    @Override
    public GatewayFilter apply(ReqApiPermissionFilterFactory.Config config) {
        return (exchange, chain) -> {

            ServerHttpRequest request = exchange.getRequest();

            String contentType = request.getHeaders().getFirst(CONTENT_TYPE);
            String method = request.getMethodValue();

            if (null != contentType && HttpMethod.POST.name().equalsIgnoreCase(method) && contentType.contains(CONTENT_TYPE_JSON)) {

                ModifyRequestBodyGatewayFilterFactory.Config modifyRequestConfig = new ModifyRequestBodyGatewayFilterFactory.Config()
                        .setContentType(ContentType.APPLICATION_JSON.getMimeType())
                        .setRewriteFunction(Map.class, Map.class, (exchange1, originalRequestBody) -> {
                            boolean isPass = validateApiPermission(exchange1, originalRequestBody);
                            if(!isPass){
                                throw new ResultException(GatewayErrorCode.PERMISSION_ERROR,
                                        ImmutableMap.of("originalRequestBody", originalRequestBody));
                            }
                            return Mono.just(originalRequestBody);
                        });

                return new ModifyRequestBodyGatewayFilterFactory().apply(modifyRequestConfig).filter(exchange, chain);
            }

            if (HttpMethod.GET.name().equalsIgnoreCase(method)) {
                Map<String,Object> query = request.getQueryParams().entrySet()
                        .stream()
                        .collect(Collectors.toMap(
                                Map.Entry::getKey,
                                Map.Entry::getValue
                        ));
                boolean isPass = validateApiPermission(exchange, query);
                if(!isPass){
                    throw new ResultException(GatewayErrorCode.PERMISSION_ERROR,
                            ImmutableMap.of("query", query));
                }
                return chain.filter(exchange.mutate().request(request).build());
            }
            return chain.filter(exchange);
        };
    }

    @Override
    public String name() {
        return "ReqApiPermission";
    }


    /**
     * 判断用户权限
     *
     * @param exchange
     * @param requestParameters
     * @return
     */
    private boolean validateApiPermission(ServerWebExchange exchange, Map<String,Object> requestParameters) {
        log.debug("接口请求参数:{}", requestParameters);

       /***实现
    }


}

至于拿到 Body 后具体要做什么,就由你自己来发挥吧

我这里是做了整个平台服务的鉴权功能。

在配置文件里添加修改

          predicates:
          - Path=/openapi/**
          filters:
          - AuthorizationSignature
          - ReqApiPermission
          - RewritePath=/openapi/(?<remaining>.*), /${remaining}

这样同样的路径可以会被两个过滤器同时过滤。

其他的实现方式可参考如下链接:Spring Cloud Gateway 之获取请求体(Request Body)的几种方式 - 码农的进击 - 博客园

Spring Cloud Gateway 是一个基于 Spring Framework 的轻量级、高性能的 API 网关,它允许开发者路由、过滤、增强和拦截网络流量,包括 HTTP 请求的 body 部分。 在 Spring Cloud Gateway 中,你可以通过 `WebFlux` 的响应拦截器机制来截获并处理请求的 body。具体步骤如下: 1. **创建拦截器**:首先,你需要自定义一个 `GlobalFilter` 或 `ExchangeFilterFunction` 类,这是 Gateway 提供的用于添加全局过滤功能的接口。 ```java import org.springframework.cloud.gateway.filter.GatewayFilter; import org.springframework.cloud.gateway.filter.GatewayFilterFunction; import org.springframework.stereotype.Component; @Component public class BodyLoggingFilter implements GatewayFilterFunction { @Override public GatewayFilter apply(String id) { return new LoggingBodyGatewayFilter(id); } } ``` 这里 `LoggingBodyGatewayFilter` 是一个具体的拦截器实现,它会记录请求的 body 内容。 2. **配置启用**:在 Gateway 的配置类(如 `application.yml` 或 `application.properties`)中注册这个过滤器,并指定需要应用的路径前缀。 ```yaml spring: cloud: gateway: filters: - Predicates: path=/api/** filter-factory: com.example.BodyLoggingFilter ``` 这表示对所有路径以 `/api` 开头的请求都会应用你的拦截器。 3. **拦截逻辑**:在 `LoggingBodyGatewayFilter` 中,你可以获取到 `ServerWebExchange` 对象,从中读取请求体的内容,然后进行处理、日志记录等操作。 ```java @Component public class LoggingBodyGatewayFilter extends ServerWebExchangeConverter { private final String id; public LoggingBodyGatewayFilter(String id) { this.id = id; } @Override protected Mono<Void> doFilter(ServerWebExchange exchange, WebFilterChain chain) { return chain.filter(exchange).flatMap(response -> { // 这里可以访问 request body Object requestBody = exchange.getAttributes().get(BodyExtractor.REQUEST_BODY_ATTRIBUTE); log.info("Received request with ID {} and body: {}", id, requestBody); return response; }); } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值