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

1,115 阅读1分钟

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}

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

其他的实现方式可参考如下链接:www.cnblogs.com/hyf-huangyo…