Spring 云网关自定义路由

Spring Cloud Gateway custom route

我正在尝试使用 spring 云网关为以下场景定义路由:

http://gateway-url/service-url?token=foo_bar => 被转发到服务 url.

http://gateway-url/service-url => 被转发到其他地方

这是我目前得到的。

@Bean
public RouteLocator customRouteLocator( RouteLocatorBuilder builder,
                                        RouteServiceForwardingFilter forwardingFilter) {

    return builder.routes()
            .route(r ->
                    r.query("token")
                            .filters(f -> {
                                f.filter(forwardingFilter, ROUTE_TO_URL_FILTER_ORDER + 1);
                                return f;
                            })
                            .uri("http://google.com:80")
                            .id("token_route"))
            .build();
}

这是我创建的过滤器

@Component
public class RouteServiceForwardingFilter implements GatewayFilter {

@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
    try {
        String forwardUrl = exchange.getRequest().getURI().toString();
        forwardUrl = forwardUrl.substring(0, forwardUrl.lastIndexOf("?"));
        exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, new URI(forwardUrl));
        return chain.filter(exchange);
    } catch (URISyntaxException e) {
        exchange.getResponse().setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR);
        return Mono.empty();
    }
}

}

所以在这里和那里看了一会儿之后,我想到了这个实现,它完全符合我的要求。

我这样编辑问题中描述的过滤器:

/**
 * Checks whether the user is using a valid token or not.
 * If the token is a valid one, a header named AUTHENTICATED is added
 * to the request and the user is forwarded to the same url with the new request.
 * If not, we forward to the same url but removing the token from it.
 */
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
    String forwardUrl = exchange.getRequest().getURI().toString();
    String queryParam = exchange.getRequest().getQueryParams().getFirst(Constants.TOKEN);
    try {

        // 1 means he is connected
        if (queryParam.equals("1")) {
            ServerHttpRequest request = exchange.getRequest().mutate().
                    header(Constants.AUTHENTICATED, "true").
                    build();
            return chain.filter(exchange.mutate().request(request).build());
        }

        if (forwardUrl.contains("?")) {
            forwardUrl = removeQueryParam(forwardUrl, Constants.TOKEN);
        }
        exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, new URI(forwardUrl));
        return chain.filter(exchange);
    } catch (URISyntaxException e) {
        return Mono.empty();
    }
}

/**
 * Removes a query param and its value from a given url
 * @param   url   the url to be modified.
 * @param   queryParam   the parameter to be removed.
 * @return  the url wihtout the query param to be removed 
 *          or the same url in case that query param isn't in that given url
 */
public static String removeQueryParam( String url, String queryParam ) {
    String result = "";
    String[] urlParts = url.split("\?");
    if (urlParts.length >= 2) {
        String[] urlParams = urlParts[urlParts.length - 1].split("&");
        for (String urlParam : urlParams) {
            String[] param = urlParam.split("=");
            if (!param[0].equals(queryParam)) {
                result += urlParam + "&";
            }
        }
        result = urlParts[0] + (result.length() > 0 ? "?" + result.substring(0, result.length() - 1) : "");
    } else {
        result = url;
    }

    return result;
}

然后我添加了这两条路由:

/**
 * Adds custom routes to the application
 */
@Bean
public RouteLocator customRouteLocator( RouteLocatorBuilder builder,
                                        SimpleLoggingFilter loggingFilter,
                                        RouteServiceForwardingFilter forwardingFilter) {

    return builder.routes()
            .route(r ->
                    r.query(Constants.TOKEN).negate()
                            .filters(f -> {
                                f.filter(loggingFilter);
                                try {
                                    f.redirect(HttpStatus.UNAUTHORIZED, new URL("http://www.example.com/401"));
                                } catch (MalformedURLException e) {
                                    LOGGER.error(e.getMessage());
                                }
                                return f;
                            })
                            .uri("http://www.example.com/401")
                            .id("WITHOUT_token"))
            .route(r ->
                    r.query(Constants.TOKEN)
                            .and()
                            .header(Constants.AUTHENTICATED).negate()
                            .filters(f -> {
                                f.filter(loggingFilter);                            
                                f.filter(forwardingFilter, Constants.ROUTE_TO_URL_FILTER_ORDER + 1);
                                return f;
                            })
                            .uri("http://localhost:8008")
                            .id("with_token"))
            .build();
}

然后就解决了。我希望这对其他人有帮助。