How to create custom Spring Cloud Gateway filter

Introduction

In this tutorial, you'll learn how to create a custom Spring Cloud Gateway filter class by extending GatewayFilterFactory. This specific example will show you how to deal with a situation where OAuth token is passed as a path variable where the request needs to be mutated to move this token to the Authorization header.

Not so long ago I had an uncommon request from my FE team and that is to somehow allow security OAuth token to be passed as path variable instead of through Authorization header. Why would someone need something like this, you could ask - they had an issue with some Angular text editor library where it would be very hard and time-consuming for them to fork it and add missing "add header" functionality. Since time was a critical factor, we decided to implement the custom filter on the Gateway service by implementing the GatewayFilterFactory interface.

Implementation

Config class is used for storing configuration properties for my custom filter. I didn't need anything like that, so I left it empty. Don't forget to annotate this class with @Component.

import java.util.Base64;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.GatewayFilterFactory;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;

@Component
public class UploadRouteFilter implements GatewayFilterFactory<UploadRouteFilter.Config> {

	@Override
	public GatewayFilter apply(Config config) {
		return ((exchange, chain) -> {
			String[] uriParts = exchange.getRequest().getURI().toString().split("/");
			String base64Token = uriParts[uriParts.length - 1];
			byte[] decodedBytes = Base64.getDecoder().decode(base64Token);
			String decodedToken = new String(decodedBytes);
			ServerHttpRequest request = exchange.getRequest().mutate().header("Authorization", decodedToken).build();
			return chain.filter(exchange.mutate().request(request).build());
		});
	}

	@Override
	public Class<Config> getConfigClass() {
		return Config.class;
	}

	@Override
	public Config newConfig() {
		Config c = new Config();
		return c;
	}

	public static class Config {}
}

So, what is exactly happening in the filter code?

First, we get URL String and split it by "/" to extract our token.

Since we had to base64 encode token before sending, we decode it to get a real token.

Then we put it in Authorization header and mutate our request that way. And that's it, we have our token on the way in Authorization header instead of a path.

@Override
public GatewayFilter apply(Config config) {
		return ((exchange, chain) -> {
			String[] uriParts = exchange.getRequest().getURI().toString().split("/");
			String base64Token = uriParts[uriParts.length - 1];
			byte[] decodedBytes = Base64.getDecoder().decode(base64Token);
			String decodedToken = new String(decodedBytes);
			ServerHttpRequest request = exchange.getRequest().mutate().header("Authorization", decodedToken).build();
			return chain.filter(exchange.mutate().request(request).build());
		});
	}

Spring cloud Gateway wont start - getConfigClass() not implemented

Recently I had this issue while migrating one Spring Cloud application to a newer version of everything - cloud version to Hoxton.SR3, boot to 2.2.6 and Java from 1.8.0.211 to 13.0.2. My Gateway app was not able to start and I had an exception log on my console.

The error log was the following:

reactor.core.Exceptions$ErrorCallbackNotImplemented: java.lang.UnsupportedOperationException: getConfigClass() not implemented
Caused by: java.lang.UnsupportedOperationException: getConfigClass() not implemented
	at org.springframework.cloud.gateway.filter.factory.GatewayFilterFactory.getConfigClass(GatewayFilterFactory.java:58) ~[spring-cloud-gateway-core-2.2.2.RELEASE.jar:2.2.2.RELEASE]
	at org.springframework.cloud.gateway.support.ConfigurationService$ConfigurableBuilder.doBind(ConfigurationService.java:187) ~[spring-cloud-gateway-core-2.2.2.RELEASE.jar:2.2.2.RELEASE]
	at org.springframework.cloud.gateway.support.ConfigurationService$AbstractBuilder.bind(ConfigurationService.java:286) ~[spring-cloud-gateway-core-2.2.2.RELEASE.jar:2.2.2.RELEASE]
	............

After investigation, I found out that after the Spring Cloud update it's required for my custom filter class to implement the getConfigClass() method from the GatewayFilterFactory interface, even though it wasn't necessary before. I didn't have any compilation error, it just crashed in runtime. So, if you had issues with this error, just override the method getConfigClass():

import java.util.Base64;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.GatewayFilterFactory;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;

@Component
public class UploadRouteFilter implements GatewayFilterFactory<UploadRouteFilter.Config> {

  @Override
  public GatewayFilter apply(Config config) {
	return ((exchange, chain) -> {
           // your custom filter code here
	});
	}

	@Override
	public Class<Config> getConfigClass() {
		return Config.class;
	}

	@Override
	public Config newConfig() {
		Config c = new Config();
		return c;
	}

	public static class Config {
	}
}

Newsletter

Please enable the javascript to submit this form

Latest