galalem
galalem

Reputation: 447

Spring Cloud Gateway Error 403: Forbidden

Context

I'm creating a MicroServices-based project. I'm using Angular 15 for Frontend and Spring 3.0.2 for Backend. I'm using Keycloack 20.0.3 for authentication for both frontend & backend. I have multiple micro-services running on different spring projects. I'm using a Spring Cloud Gateway as an entry point for my front-end so I don't need to manage ports with angular. I'm also using Eureka for my gateway.

Note: My gateway is configured with YAML file only. It is NOT configured with neither Spring Security nor Keycloak. CORS are disabled.

spring:
  application:
    name: gateway
  cloud:
    gateway:
      globalcors:
        corsConfigurations:
          '[/**]':
            allowedOrigins: "https://localhost:4200"
            allowedMethods: "*"
            allowedHeaders: "*"
      routes:
      - id: users
        uri: lb://micro-service-1/api/users
        predicates:
        - Method=GET,POST,PUT,DELETE
        - Path=/api/users/**
      - id: assets
        uri: lb://micro-service-2/api/assets
        predicates:
        - Method=GET,POST,PUT,DELETE
        - Path=/api/assets/**

server:
  port: 8100
eureka:
  client:
    registerWithEureka: true
    serviceUrl:
      defaultZone: http://localhost:8761/eureka

My problem

I'm building a BREAD api (Browse, Read, Edit, Add, Delete). Browse & Read (Http GET requests in general) Works fine but the rest throws 403 Error in Angular.

Note: I tried using Postman for testing and somehow NO Error is thrown

When I tried to skip the gateway and directly communicate with the micro-service every everything works fine, NO Error is thrown

Note: My Backend is RESTful APIs Only, Keycloak Auth handles all login/logout/register...

My failed solutions

My gateway is NOT configured with neither Spring Security nor Keycloak. CORS are disabled.

1. Tried Disabling CSRF on gateway

security.enable-csrf: false

It didn't work!

2. Tried adding Spring Security & Disabling CSRF on gateway

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-security</artifactId>
</dependency>
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http.csrf().disable().cors().disable().httpBasic().disable()
            .authorizeHttpRequests(auth -> auth.anyRequest().permitAll());
        return http.build();
    }

Cridentials still required & Conflict with Keycloak

3. Tried configuring Keycloak for gateway

Keycloak required Spring Web resulted in Conflict with Spring Cloud Gateway


I Spent 2 weeks, Can't find any thing on the internet. Everything is either depricated or simply doesn't work. any clue?

Note: I know I can use angular Proxy Config to replace the gateway but this breaks the Spring cloud architecture, and is only a work-around for dev purposes

Upvotes: 4

Views: 4442

Answers (2)

ch4mp
ch4mp

Reputation: 12835

I won't answer to your current problem, but try to give you elements for the next steps: I guess that in the end, you want your app to be secured (implement some sort of user based access-control) and as you mention Keycloak it's going to involve OpenID.

Keycloak adapters for spring were deprecated in early 2022 and are not compatible with Spring Boot 3. Do not use it, even in servlet applications (and , as you already figured out, spring-cloud-gateway is a reactive application, so this are two good reasons for not using it).

Are you aware that storing OAuth2 tokens in public-clients (browser applications not solely Angular, but also React, Vue, etc.) is now discouraged? The fact is that if you inspect any major web service (Gmail, Facebook, Github, etc.) you won't find any OAuth2 access-token but just a regular session (implemented with Secured Http-Only cookies).

Have you heard of the BFF (backend for frontend) pattern? To help you make your own idea, I quite liked this article from the author of my prefered OpenID client lib for Angular (which isn't of any use when you implement BFF).

If you intend to configure your gateway as BFF, you need spring-boot-starter-oauth2-client, not spring-boot-starter-security. I wrote a tutorial for that on Baeldung

Upvotes: 2

galalem
galalem

Reputation: 447

After some playing around with different configurations I ended up with this one working.

Gateway

spring:
  application:
    name: gateway
  cloud:
    gateway:
      globalcors:
        corsConfigurations:
          '[/**]':
            allowedOrigins: "*"
            allowedMethods: "*"
            allowedHeaders: "*"
      routes:
      - id: users
        uri: lb://micro-service-1
        predicates:
        - Path=/api/users/**
        filters:
        - RewritePath=/api/users/(?<segment>.*),/api/users/$\{segment}
      - id: assets
        uri: lb://micro-service-2
        predicates:
        - Path=/api/assets/**
        filters:
        - RewritePath=/api/assets/(?<segment>.*),/storage/$\{segment}

server:
  port: 8100
eureka:
  client:
    registerWithEureka: true
    serviceUrl:
      defaultZone: http://localhost:8761/eureka

CHANGES: in uri I set the base uri only, removed methods from routes and added RewritePath filters

Gateway Projet have no authentication nor security library included in POM. Only Spring Cloud

Micro-Services

In each individual micro-service I added the following SecurityConfig class

@Configuration
@EnableWebSecurity
class SecurityConfig {

    @Bean
    protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
        return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
    }

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http.csrf().disable()
            .authorizeHttpRequests(auth -> auth.anyRequest().authenticated());
        http.oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt);
        return http.build();
    }
}

Upvotes: 1

Related Questions