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

2022-07-07 08:37:21

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}

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

  • 作者:雪中鱼01
  • 原文链接:https://blog.csdn.net/songkai558919/article/details/121099153
    更新时间:2022-07-07 08:37:21