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());
		});
	}