A Trip Through Spring Cloud Gateway

API Gateways in Spring

Audience

This article is aimed at developers wanting to learn more about Spring Cloud Gateway. Specifically we will be using it to extract and transform data from API request headers, but the overall techniques are recyclable for a number of purposes.

It will assume a small amount of knowledge of HTTP, JWT tokens and Spring, but the overall gist should be accessible to most.

Argument

Before we begin in earnest, what is an API Gateway?

Typically an API Gateway acts as a reverse proxy, sitting between a client and a set of services, offering cross-cutting concerns. This may be authorising the client, forwarding requests to the appropriate service, or adding supplementary information. The client makes requests to the API Gateway, which in turn makes an appropriate set of requests to other services.

In this diagram we have a client making requests to an API Gateway, which calls another service to offer some assistance with the request (perhaps authorisation). Once this is done we can see the API Gateway calls a set of backend services.

Spring Gateway then comprises of three core parts: routes, predicates and filters. Below is a short introduction to each, we will expand on them in turn.

  • Route: These are the core part of gateway. They have an Id, a URI representing where requests should be forwarded, predicates and filters. We match a route if the predicate is true.
  • Predicate: This lets you match on parts of an HTTP request. Perhaps headers or parameters.
  • Filter: These are used to modify requests and responses before or after forwarding your request.

The components then fit together as in the below diagram.

Fitting the Spring Gateway components together

A client makes a request to the gateway. If this request matches a route then it’s sent to the web handler. This handler pushes the request through a request-specific filter chain and then out to the destination service. Note how the filters are split in two, this represents the filtering happening both on the request and the response.

Conceptually this covers how Spring Gateway works, however I appreciate this seems very abstract. Let’s clarify with an example.

First of all we need to go to the Spring Initializr and start up a new project. I’ve used the dependencies below.

Dependencies needed for our example project

We will also be using the auth0 library for dealing with JWT tokens, so we will need to add the below dependency to our project.

Now we have our Spring project generated we will introduce our first route predicate. This can be done by adding the below configuration in the application.yml file.

This will match all requests to the v1/users path with a POST request and a cookie called user_token which will contain a JWT token. We are going to use this token to validate the user with our downhill TokenValidation filtering, specified using the filters option.

If we were so inclined we could use regex to make sure the contents of the cookie was always a JWT token, but as we will be validating later it makes sense to leave it as any string.

Although we won’t cover it in this blog post, we could introduce our own, custom predicate if we wanted to. Perhaps we would like to confirm the request body contained a certain value, in which case we would look into extending the AbstractRoutePredicateFactory.

Let’s move on to implement our token validation. To aid this I’ll offer a quick refresher on JWT tokens (taken from my article here).

JWT Tokens are encoded, cryptographically signed JSON data. The idea is that this is JSON guaranteed to come from a certain source.

So what do these tokens look like? An example JWT token is explained below.

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

This essentially consists of three parts, separated by a .

The header: This comprises is the algorithm used to sign the JSON, and the type of token (JWT in our case).

{
"alg": "HS256",
"typ": "JWT"
}

The payload: This contains the ‘claims’. Claims are statements about an entity, as well as some additional data. An example payload is found below.

{
"sub": "123456789",
"name": "John Smith",
"iat": 1516239021
}

The signature: To create the signature part you have to take the encoded header, the encoded payload, a secret, the algorithm specified in the header, and combine them.

signingAlgorithm(
base64UrlEncodedHeader + "." +
base64UrlEncodedPayload,
secret)

This signature can then be used to verify the message hasn’t changed, and if the secret is a private key, we can also use it to verify the sender!

An important point to note is that these tokens are still visible to everyone, we can just guarantee they haven’t been tampered with. So don’t put anything sensitive in them!

To implement our token validation we will create a custom filter. This filter will take in requests, look for a given cookie, extract the token from it, validate it, then use information from the token to add a header to the request. All of it can be done in a single file, contained below.

Let’s try it out! We need to first generate a valid JWT token, I used the site here. If you read the code you’ll notice we need the token to be signed using the secret our-secret, and we’re expecting a claim userId. This can be set up in the token as below.

Setting up a JWT token to use as a cookie

I used Postman to attach the cookie and send on a forward request (you’ll need a downstream service to send to, in our example it was https://backend-services.com/v1/users). You’ll notice in your backend service you receive the new x-user-id header!

Conclusion

In conclusion we have demonstrated how Spring Gateway can be used as an API Gateway, with a worked example for authorisation using a JWT token.

Principal Software Engineer at the BBC