[JAVA] Send a request to the backend after authenticating with Spring Cloud Gateway

Purpose

For various WebUI tools such as Locust that can be used without the need for user authentication I wanted to use it after inserting my own authentication. I found it troublesome to implement the authentication function in each tool as the types of tools increased.

Therefore, place the Spring Cloud Gateway application in front of each tool. Here, all the requests for each tool are accepted and the original authentication is performed. If the authentication is OK, I tried to send the request to the backend tool.

Project setup

Add "Gateway" to Dependencies in Spring initializr, create a template and download it.

Implementation 1: First, request distribution without authentication

First of all, without authentication function, simply accept the request with Sprin Cloud Gateway, Let's try to distribute the request to the backend tool.

Up to this point, you don't need to implement Java class, you can do it just by setting application.yaml.

This time the Spring Cloud Gateway application listens on port 8080 and http://localhost:8080 Make it accessible with.

The URL of the backend Web UI tool is http://localhost:9001 and http://localhost:9002 will do.

application.yaml

#Spring Cloud Gateway port number settings(You don't have to)
server:
  port: 8080

#Spring Cloud Gateway main settings
spring:
  cloud:
    gateway:
      routes:
        # -----------------------------------------------------
        # http://localhost:8080/tool1/hoge/fuga/...Request
        # http://localhost:9001/hoge/fuga/...Flow to
        # -----------------------------------------------------
        - id: tool1
          #Proxy destination
          uri: http://localhost:9001
          #routing
          predicates:
            - Path=/tool1/**
          #filter(Insert path rewriting and original processing)
          filters:
            - StripPrefix=1 #Cut off the beginning of the path. In this case"/tool1"Get rid of
        # -----------------------------------------------------
        # http://localhost:8080/tool2/hoge/fuga/...Request
        # http://localhost:9002/hoge/fuga/...Flow to
        # -----------------------------------------------------
        - id: tool2
          #Proxy destination
          uri: http://localhost:9002
          #routing
          predicates:
            - Path=/tool2/**
          #filter(Insert path rewriting and original processing)
          filters:
            - StripPrefix=1 #Cut off the beginning of the path. In this case"/tool2"Get rid of

The important one is spring.cloud.gateway.routes, and the outline of each item is as follows.

item Contents
id Set any ID
uri To send the request(Back end)To set
predicates Define what kind of request is made to Spring Cloud Gateway to apply to this routing rule.
filters Specify when inserting processing before or after sending a request to the backend. You can also use your own filter class(See below)

Please refer to Spring Cloud Gateway Official Document to find out what the built-in predicates and filters are. I think.

Operation check

Instead of the actual WebUI tool, launch a stub server that returns a fixed string with the nc command.

$ (echo "HTTP/1.1 200 ok"; echo; echo "hello") | nc -l 9001

Then make a curl request to Spring Cloud Gateway.

$ curl -v http://localhost:8080/tool1/hoge/fuga

If the Spring Cloud Gateway settings are correct, localhost: 8080 / tool1 / hoge / fuga request It should flow to localhost: 9001 / hoge / fuga.

result

The contents of the request that came to the stub server are as follows. You can see that the URL path is / hoge / fuga instead of / tool / hoge / fuga.

$ (echo "HTTP/1.1 200 ok"; echo; echo "hello") | nc -l 9001
GET /hoge/fuga HTTP/1.1
Host: localhost:8080
User-Agent: curl/7.64.1
Accept: */*
Forwarded: proto=http;host="localhost:8080";for="0:0:0:0:0:0:0:1:50024"
X-Forwarded-For: 0:0:0:0:0:0:0:1
X-Forwarded-Proto: http
X-Forwarded-Prefix: /tool1
X-Forwarded-Port: 8080
X-Forwarded-Host: localhost:8080
content-length: 0

The result of the curl request is as follows, You can see that Spring Cloud Gateway is returning the response of the stub server on the backend.

$ curl -v http://localhost:8080/tool1/hoge/fuga
> GET /tool1/hoge/fuga HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.64.1
> Accept: */*
>
< HTTP/1.1 200 OK
< transfer-encoding: chunked
<
hello

Implementation 2: Add your own filter to authenticate

Next, create your own authentication filter and apply it. This time, extract the value of the "Authorization" key from the HTTP request header If the value is "xxx", authentication is OK and a request is sent to the backend. Otherwise, try creating a filter that returns a 401 Unauthorizaed response as authentication NG.

MyAuthFilter.java The implementation of the filter is as follows, and implement the class that inherits AbstractGatewayFilterFactory.

@Component
public class MyAuthFilter extends AbstractGatewayFilterFactory<MyAuthFilter.Config> {
    public MyAuthFilter() {
        super(Config.class);
    }

    @Override
    public GatewayFilter apply(Config config) {
        return (exchange, chain) -> {
            //Get Authorization Header
            ServerHttpRequest request = exchange.getRequest();
            String authorizationHeader = Optional.ofNullable(request.getHeaders().get("Authorization"))
                    .map(h -> {return h.get(0);}).orElse("");

            //If the Authorization header is xxx, the request will be sent as it is with successful authentication.
            //Otherwise returns a 401 Unauthorized response
            if(authorizationHeader.equals("xxx")) {
                return chain.filter(exchange.mutate().request(request).build());
            } else {
                ServerHttpResponse response = exchange.getResponse();
                response.setStatusCode(HttpStatus.UNAUTHORIZED);
                return response.setComplete();
            }
        };
    }

    public static class Config {

    }
}

application.yaml Just creating your own filter class as described above will not be reflected yet. If you want to apply your own filter, go to the filters item in application.yaml. I will write the class name of this unique filter.

The whole picture of application.yaml is as follows, and only the two lines that say "[Add]" are added.

#Spring Cloud Gateway main settings
spring:
  cloud:
    gateway:
      routes:
        # -----------------------------------------------------
        # http://localhost:8080/tool1/hoge/fuga/...Request
        # http://localhost:9001/hoge/fuga/...Flow to
        # -----------------------------------------------------
        - id: tool1
          #Proxy destination
          uri: http://localhost:9001
          #routing
          predicates:
            - Path=/tool1/**
          #filter(Insert path rewriting and original processing)
          filters:
            - MyAuthFilter  # [add to]Insert your own authentication filter
            - StripPrefix=1 #Cut off the beginning of the path. In this case"/tool1"Get rid of
        # -----------------------------------------------------
        # http://localhost:8080/tool2/hoge/fuga/...Request
        # http://localhost:9002/hoge/fuga/...Flow to
        # -----------------------------------------------------
        - id: tool2
          #Proxy destination
          uri: http://localhost:9002
          #routing
          predicates:
            - Path=/tool2/**
          #filter(Insert path rewriting and original processing)
          filters:
            - MyAuthFilter  # [add to]Insert your own authentication filter
            - StripPrefix=1 #Cut off the beginning of the path. In this case"/tool2"Get rid of

#Spring Cloud Gateway port number settings(You don't have to)
server:
  port: 8080

Operation check

Launch the stub server on port 9001 as before, Request without Authorization header as below Make a curl request with an Authorization header.

$ curl -v http://localhost:8080/tool1/hoge/fuga
$ curl -H 'Authorization: xxx' -v http://localhost:8080/tool1/hoge/fuga

result

If you request without the Authrozation header, as implemented in MyAuthFilter, A 401 Unauthorized response is returned.

$ curl -v http://localhost:8080/tool1/hoge/fuga
> GET /tool1/hoge/fuga HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.64.1
> Accept: */*
>
< HTTP/1.1 401 Unauthorized
< content-length: 0
<

You can also see that no request has arrived on the stub server side.

$ (echo "HTTP/1.1 200 ok"; echo; echo "hello") | nc -l 9001
* No change

Next, when you request by setting the value of xxx in the Authrozation header, The hello string returned by the stub server is returned.

$ curl -H 'Authorization: xxx' -v http://localhost:8080/tool1/hoge/fuga
> GET /tool1/hoge/fuga HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.64.1
> Accept: */*
> Authorization: xxx
>
< HTTP/1.1 200 OK
< transfer-encoding: chunked
<
hello

You can see that the request is also coming to the stub server side.

$ (echo "HTTP/1.1 200 ok"; echo; echo "hello") | nc -l 9001
GET /hoge/fuga HTTP/1.1
Host: localhost:8080
User-Agent: curl/7.64.1
Accept: */*
Authorization: xxx
Forwarded: proto=http;host="localhost:8080";for="0:0:0:0:0:0:0:1:50517"
X-Forwarded-For: 0:0:0:0:0:0:0:1
X-Forwarded-Proto: http
X-Forwarded-Prefix: /tool1
X-Forwarded-Port: 8080
X-Forwarded-Host: localhost:8080
content-length: 0

For the time being, it seems possible to control whether authentication is OK or NG and whether or not to send a request to the back end.

reference

site Overview
Play with Spring Cloud Gateway First read page
Spring Cloud Gateway Official Spring documentation. There is a lot of information about the built-in Predicates and Filters. There is also a little explanation about the self-made Filter.
Spring Cloud Gateway - Creating Custom Route Filters (AbstractGatewayFilterFactory) Implementation of self-made Filter and application.For the setting method in yaml, I mainly referred to this page.

Recommended Posts

Send a request to the backend after authenticating with Spring Cloud Gateway
Send a pull request to GitHub
Oauth2 authentication with Spring Cloud Gateway
Configure microservices with Spring Cloud (4): API Gateway
How to check before sending a message to the server with Spring Integration
03. I sent a request from Spring Boot to the zip code search API
Send a notification to slack with the free version of sentry (using lambda)
Let's write a proxy integrated Lambda function of Amazon API Gateway with Spring Cloud Function
[Rails] Processing after adding a column to the devise table
How to take a screenshot with the Android Studio emulator
How to request a CSV file as JSON with jMeter
The first WEB application with Spring Boot-Making a Pomodoro timer-
To receive an empty request with Spring Web MVC @RequestBody
I tried to express the result of before and after of Date class with a number line
Try to imitate the idea of a two-dimensional array with a one-dimensional array
I want to understand the flow of Spring processing request parameters
Read the file under the classpath as a character string with spring
Logic to draw a circle with ASCII art on the console
Connecting to a database with Java (Part 1) Maybe the basic method
Replace with a value according to the match with a Java regular expression
Sample code to unit test a Spring Boot controller with MockMvc
Connect IoT devices to the cloud using gateway and subdevice scenarios
I wanted to make JavaFX programming easier with the Spring Framework
Things to forget when intercepting a request with Android's WebView # shouldInterceptRequest