Skip to main content

Reverse Proxy

Repository

https://github.com/Mehdi-HAFID/reverse-proxy

Credit

All credit for the BFF and reverse proxy implemented in Nidam goes to Jérôme Wacongne and his article in baeldung.com OAuth2 Backend for Frontend With Spring Cloud Gateway. The BFF and reverse proxy in Nidam is pretty much a copy paste from his article. I strongly recommend to go and read his article.

Why do we need a Reverse Proxy?

The SPA and the BFF -discussed on the next pages- must be served to the user through the same domain. Quoted from the article:

We need the same origin for an SPA and its BFF because:

  • the requests are authorized with session cookies between the frontend and the BFF
  • Spring session cookies are flagged with SameSite=Lax

Meaning the SPA and the BFF both must share the same domain. Otherwise, the Session Cookie would not work.

3 applications will be served with the reverse proxy: the SPA React application, the Authorization Server, and the BFF.

Reverse Proxy Configuration

The reverse proxy job is very simple if the context path is:

  • /react-ui then redirect to the React application.
  • /auth then redirect to the authorization server.
  • /bff then redirect to the BFF.

In development, the reverse proxy is deployed through http://localhost:7080. All URLs requests go through here.

pom.xml

pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.0</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>nidam</groupId>
<artifactId>reverse-proxy</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>reverse-proxy</name>
<description>reverse-proxy</description>
<properties>
<java.version>17</java.version>
<spring-cloud.version>2023.0.0</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

Routing

All of the logic we want the reverse proxy to achieve is simply stated in the application.properties

Applications accessed through: React http://localhost:7080/react-ui Authorization Server http://localhost:7080/auth BFF http://localhost:7080/bff

src/main/resources/application.yml
scheme: http
hostname: localhost
reverse-proxy-port: 7080

react-port: 4001
react-prefix: /react-ui
react-uri: ${scheme}://${hostname}:${react-port}${react-prefix}

authorization-server-port: 4002
authorization-server-prefix: /auth
authorization-server-uri: ${scheme}://${hostname}:${authorization-server-port}${authorization-server-prefix}

bff-port: 7081
bff-prefix: /bff
bff-uri: ${scheme}://${hostname}:${bff-port}

server:
port: ${reverse-proxy-port}

spring:
cloud:
gateway:
default-filters:
- DedupeResponseHeader=Access-Control-Allow-Credentials Access-Control-Allow-Origin
routes:
# SPAs assets
- id: react-ui
uri: ${react-uri}
predicates:
- Path=${react-prefix}/**
# Authorization-server
- id: authorization-server
uri: ${authorization-server-uri}
predicates:
- Path=${authorization-server-prefix}/**
# BFF
- id: bff
uri: ${bff-uri}
predicates:
- Path=${bff-prefix}/**
filters:
- StripPrefix=1

Note that calls to the BFF are stripped of the /bff path after the redirection. For example, when calling http://localhost:7080/bff/something the reverse proxy calls http://localhost:7081/something with /bff removed. That's not the case with the React application or the authorization server. They both keep the same path with no changes.

http://localhost:7080/auth/something => http://localhost:4002/auth/something

http://localhost:7080/react-ui/something => http://localhost:4001/react-ui/something

This is done through spring.cloud.gateway.routes[2].filters: StripPrefix=1

Explaining an authorization server property

On the authorization server page, I did not explain these 2 properties:

src/main/resources/application.properties
server.servlet.contextPath=/auth		# 1
server.forward-headers-strategy=framework # 2
  1. contextPath is simply serving the authorization server with an /auth path. so the URL is http://localhost:4002/auth
  2. This will use the reverse proxy URL in addition to the path context, because there is a problem with spring auth server link. You can read my comment about the issue here