Skip to content
Her Tech Corner Her Tech Corner
  • Home
  • Learn Spring
    • Spring Frameworks
    • Java Core
  • Microservices
  • Cloud
  • My Journey
    • Machine Learning
    • Linux
    • Algorithm
    • Book Notes
  • Tools I use
Her Tech Corner
Her Tech Corner

Spring Cloud Gateway as an OAuth2 Client

Posted on September 25, 2023November 14, 2023 By Hoai Thu

Last updated on November 14th, 2023

In my previous post, I introduced the API Gateway Pattern in Microservices. Let’s take a look at the components in our system, as demonstrated below.

We have API Gateway which proxies our requests to the authorization server and two services in our services. The API gateway has to proxy requests from clients to the authorization server and profile and point services.

As mentioned earlier, I will attempt to implement each of the functions of the API Gateway Pattern. In this post, I will start with the implementation of security concerns using Spring Cloud Gateway. Further exploration of the remaining concerns such as observability (monitoring logging) or resiliency (rate limiter), will be covered in upcoming posts.

Table of Contents

Toggle
  • Start our Authorization Server
  • Configure the resources server
  • Configure Spring Cloud Gateway as an OAuth2 Client
  • Configure routing on Spring Cloud Gateway
    • Introduce Spring Cloud Gateway architecture
    • Spring Cloud Gateway Implementation
    • Testing
  • Next steps

Start our Authorization Server

Let’s launch our Authorization Server, which was implemented in my previous post. Add a docker-compose.yml file and start the server at the port :9000:

docker-compose.yml


services:
  auth_database:
    image: postgres
    container_name: auth_database
    ports:
      - "5432:5432"
    environment:
      - POSTGRES_PASSWORD=123456
      - POSTGRES_DB=auth_server
    restart: always
  authorization:
    build: ./
    container_name: authorization_service
    ports:
      - "9000:9000"
    environment:
      - AUTH_POSTGRES_USER=postgres
      - AUTH_POSTGRES_PASSWORD=123456
      - POSTGRES_HOST=host.docker.internal
      - POSTGRES_DATABASE=auth_server
    depends_on:
      - auth_database

Don’t forget to include both client credentials and user credentials in our database, as they will be used in the subsequent steps.

Configure the resources server

To secure the endpoints of the resources server, we first add security-related dependencies as shown below:


<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>

I configured the jwk-set-endpoint as /oauth2/jwks with an asymmetric keys pair in the authorization server previously. Therefore, all that’s required on the resource server is to add the configuration below to discover the public keys for JWT token validation.


spring:
  application:
    name: profile-service

  security:
    oauth2:
      resourceserver.jwt.jwk-set-uri: ${OAUTH2_CLIENT_ISSUER_URI:http://localhost:9000}/oauth2/jwks

The next step is a simple configuration for our SecurityFilterChain to authorize all incoming requests and validate the token.


@Configuration
@EnableWebSecurity
public class SecurityConfig {

  @Bean
  SecurityFilterChain configureFilterChain(HttpSecurity http) throws Exception {
    http.authorizeHttpRequests(
            authorizeRequests ->
                authorizeRequests
                    .requestMatchers("/webjars/**")
                    .permitAll()
                    .anyRequest()
                    .authenticated())
        .oauth2ResourceServer(configure -> configure.jwt(Customizer.withDefaults()));
    http.csrf(AbstractHttpConfigurer::disable);
    return http.build();
  }
}

Finally, let’s create a simple endpoint for testing purposes, as shown below:


@RestController
@RequestMapping
public class UserController {

  @GetMapping("/me")
  public String getMyInfo() {
    return "This is my info";
  }
}

Configure Spring Cloud Gateway as an OAuth2 Client

To enable Spring Cloud Gateway to act as an OAuth2 Client, we include spring-boot-starter-oauth2-client dependency.


<!--Security-->
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>
<!-- Gateway-->
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>

Next, we need to set up the client information in the YAML configuration as follows. registrationId should be set to spring, and issuer-uri should point to our authorization server.

Remember that the client credentials should be registered to the Authorization Server above.


spring:
  security:
    oauth2:
      client:
        registration:
          spring:
            provider: spring
            client-id: clientB
            client-secret: secret
            authorization-grant-type: authorization_code
            client-authentication-method: client_secret_basic
            redirect-uri: '{baseUrl}/login/oauth2/code/{registrationId}'
            scope: read,openid
        provider:
          spring:
            issuer-uri: ${OAUTH2_CLIENT_ISSUER_URI:http://127.0.0.1:9000}

Let’s configure basic Spring Security.

The central place for defining and configuring security policies in Spring Security is a SecurityWebFilterChain bean. It tells the framework which filters should be enabled, so we can use this bean to define and configure security policies for the application. We can build a SecurityWebFilterChain bean through DSL provided by ServerHttpSecurity.

The ServerHttpSecurity object provides a convenient DSL to configure Spring Security and build a SecurityWebFilterChain bean. With authorizeExchange() you can define access policies for any request (called exchange in Reactive Spring). With oauth2Login(), you can configure an application to act as an OAuth2 Client and also authenticate users.

Next, annotate the SecurityConfig with @EnableWebFluxSecurity to enable authentication for all the exchanges.


@Configuration
@EnableWebFluxSecurity
public class SecurityConfig {
  @Bean
  SecurityWebFilterChain configureFilterChain(ServerHttpSecurity http) {
    return http.authorizeExchange(
            exchange ->
                exchange
                    .anyExchange()
                    .authenticated())
        .oauth2Login(Customizer.withDefaults())
        .csrf(ServerHttpSecurity.CsrfSpec::disable)
        .build();
  }
}

Configure routing on Spring Cloud Gateway

Introduce Spring Cloud Gateway architecture

Spring Cloud Gateway offers an alternative to traditional proxies. It is built on top of the Reactor project and provides a non-blocking API for routing and filtering HTTP requests. With SCG, we can establish routing rules and filters that have the capability to adjust or enhance the request and response before it reaches the backend services.

Let’s take a look at its overall architecture:

HandlerMapping

The Gateway Handler Mapping component is responsible for mapping incoming requests to the suitable handler based on the routing rules. When a request reaches the gateway, the first thing the gateway does is match the request with each of the routes based on the defined predicates.

Filters

The Gateway Filter component is responsible for filtering the request and response before it reaches the backend service. Filters can be used to modify the request and response headers and add authentication, logging, and monitoring.

There are many out-of-the-box filters to modify the request header and the body. Pre-filters are applied for a particular route, while global filters are applied to all the route requests. In the upcoming implementation, we will apply global filters to authentication and authorization for all requests. After the downstream service generates a response, the post-filters can be used to adjust the response. For instance, it can apply a checksum to the response header to ensure that the response is not tampered with during transit, protecting against middleman attacks.

Spring Cloud Gateway Implementation

Finally, we configure routing either through Java code or YAML, as shown below.

Through defining a @Bean:


import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.GatewayFilterSpec;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class GatewayConfig {

  @Bean
  RouteLocator gateway(RouteLocatorBuilder rlb) {
    return rlb.routes()
        .route(
            rs ->
                rs.path("/me") // path at gateway
                    .filters(
                        gatewayFilterSpec -> {
                          gatewayFilterSpec.tokenRelay();
                          return gatewayFilterSpec.setPath("/me"); // path at profile service
                        })
                    .uri("http://localhost:8080")) // profile serverice
        .build();
  }
}

Or through YAML:


server:
  port: 8082
spring:
  cloud:
    gateway:
      default-filters:
        - TokenRelay
      routes:
        - id: profile-route
          uri: ${PROFILE_SERVICE_URL:http://127.0.0.1:8080}
          predicates:
            - Path=/me

All the setup is complete. Let’s examine how it works before testing.

After the user is authenticated, the authorization server provides the JWT to the gateway service. The gateway service keeps track of JWT and associates it with a specific user session using a session cookie. Subsequently, whenever the Gateway forwards a request downstream, it must include that JWT in the request. It can be done via TokenRelay Filter. When a request comes into the gateway service, the TokenRelay filter extracts the session from the request and retrieves the access token corresponding to the session. The resource service (profile service) only needs to validate that token for security purposes.

Testing

Let’s summarize what we have done thus far:

  • 9000: Authorization server
  • 8082: Gateway service
  • 8080: Profile service

Here is our scenario: a user is trying to access a protected endpoint exposed by Gateway Service. The application redirects the user to a login page and asks the user to provide a username and password. Then, the Gateway Service checks the user credentials with the Authorization Service, obtains the JWT, and starts an authenticated session with the browser. The user session is kept alive through a cookie whose value is provided by the browser with each HTTP request (session cookie). Gateway Service maintains a mapping between the session identifier and access token.

First, make a call to this endpoint via the gateway:http://127.0.0.1:8082/me. The service will redirect the user to the login page at http://127.0.0.1:9000/login

After receiving the login request, Gateway obtains the JWT from the authorization server. Subsequently, Gateway creates a web session and Authentication bean. Finally, it returns a session ID to the external client. The external client is using a cookie with a session ID to authorize requests. Gateway then forwards the request to the downstream Profile service.

Finally, if you take a look at the browser, you’ll notice that no token is exposed. It’s good!!! The interaction between the browser and the backend now relies on session cookies, with the backend controlling the authentication flow. This approach is recommended to mitigate the risk of token exposure.

Next steps

In this post, I’ve covered the way to set up a Spring Gateway Service as an OAuth2 Client. The source code can be found on the GitHub repo.

In the upcoming posts, I’ll sequentially implement other aspects such as monitoring or rate limiting in Spring Cloud Gateway.

Happy coding!!!

Related

Learn Spring Microservices Spring Frameworks API GatewayAuthorization ServerMicroservicesOauth2

Post navigation

Previous post
Next post

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

Categories

  • Algorithm (2)
  • Cloud (1)
  • Database (1)
  • Java Core (1)
  • Learn Spring (1)
  • Machine Learning (1)
  • Microservices (4)
  • Spring Frameworks (6)
  • Tools I use (1)

Recent Posts

  • Leetcode challenges 2023-2024December 12, 2023
  • Docker Essentials: A Novice’s JourneyOctober 7, 2023
  • Spring Cloud Gateway as an OAuth2 ClientSeptember 25, 2023
  • An Introduction to Prompt Engineering with LLMsSeptember 18, 2023
  • A brief overview of API Gateway Pattern in MicroservicesSeptember 7, 2023

Tags

Algorithms AOP API Gateway Authorization Server Cloud Database Design Pattern Docker Java8 Jwt Leetcode LLMs Microservices Oauth2 Prompt Refactoring Spring

©2025 Her Tech Corner | WordPress Theme by SuperbThemes