Why this public endpoints returns 404?

50 views Asked by At

I've been trying to figure this out for days and getting really crazy.

I have two microservices in spring cloud, one working as a gateway with zuul, running at localhost:8090, and the other runs at localhost:8002. This one hosts an endpoint which runs good if I hit directly to this microservice:

Success case

However, if I try to perform the same against zuul gateway (this microservice also works as authorization server) the result is:

Failing case

Files of interest in zuul gateway microservice:

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.0.2</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.contractar</groupId>
    <artifactId>microservicio-gateway</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>microservicio-gateway</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>19</java.version>
         <maven.compiler.release>19</maven.compiler.release>
         <spring-cloud.version>2022.0.2</spring-cloud.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
            <version>2.2.0.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.security.oauth</groupId>
            <artifactId>spring-security-oauth2</artifactId>
            <version>2.3.8.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-oauth2-jose</artifactId>
            <version>6.0.3</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-oauth2-resource-server</artifactId>
            <version>6.0.3</version>
        </dependency>
        <dependency>
            <groupId>com.auth0</groupId>
            <artifactId>java-jwt</artifactId>
            <version>3.11.0</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webflux</artifactId>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>    
                    <mainClass>com.contractar.microserviciogateway.MicroservicioGatewayApplication</mainClass>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                        <configuration>
                            <classifier>exec</classifier>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>build-helper-maven-plugin</artifactId>
                <version>3.2.0</version>
            </plugin>
        </plugins>
    </build>
</project>

Main class:

package com.contractar.microserviciogateway;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;

@SpringBootApplication(exclude={DataSourceAutoConfiguration.class})
@EnableZuulProxy
public class MicroservicioGatewayApplication {

    public static void main(String[] args) {
        SpringApplication.run(MicroservicioGatewayApplication.class, args);
    }

}

application.properties:

spring.application.name=microservicio-gateway
server.port=8090

app.security.jwt.keystore-location=keys/keystore.jks
app.security.jwt.keystore-password=contractar
app.security.jwt.key-alias=keyAlias
app.security.jwt.private-key-passphrase=contractar

zuul.ignored-services='*'

zuul.routes.usuarios.service-id=microservicio-usuario
zuul.routes.microservicio-usuario.path=/usuarios/**
zuul.routes.microservicio-usuario.url=http://localhost:8002

hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds: 90000
ribbon.ConnectTimeout: 9000
ribbon.ReadTimeout: 30000

ribbon.eureka.enabled=false

#MOVER DESPUES ESTO A AMBIENTE DE DEV
logging.level.org.springframework.security=TRACE

management.endpoints.web.exposure.include=*

Security config:

package com.contractar.microserviciogateway.security;

import java.util.Arrays;

import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
import org.springframework.security.config.annotation.web.configurers.oauth2.server.resource.OAuth2ResourceServerConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;

@Configuration
@EnableWebSecurity
public class WebSecurityConfig{

    @Bean
    public FilterRegistrationBean<CorsFilter> corsFilter() {
        FilterRegistrationBean<CorsFilter> bean = new FilterRegistrationBean<CorsFilter>(
                new CorsFilter(corsConfigurationSource()));
        bean.setOrder(Ordered.HIGHEST_PRECEDENCE);
        return bean;
    }

    @Bean
    public CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration corsConfig = new CorsConfiguration();
        corsConfig.setAllowedOrigins(Arrays.asList("*"));
        corsConfig.setAllowedMethods(Arrays.asList("POST", "GET", "PUT", "DELETE", "OPTIONS"));
        corsConfig.setAllowCredentials(true);
        corsConfig.setAllowedHeaders(Arrays.asList("Authorization", "Content-type"));

        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", corsConfig);

        return source;
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {

        http.formLogin().usernameParameter("email");

        http.headers().httpStrictTransportSecurity().disable();

        http.cors().configurationSource(corsConfigurationSource()).and().csrf().disable().sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().authorizeHttpRequests(
                (requests) -> requests.requestMatchers(new AntPathRequestMatcher("/usuarios/**"), new AntPathRequestMatcher("/login"),
                new AntPathRequestMatcher("/error"))
                .permitAll().anyRequest().authenticated()
                )
        .oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt);

        return http.build();

    }
    
    @Bean
    public WebSecurityCustomizer webSecurityCustomizer() {
        return (web) -> web.ignoring().requestMatchers("/error");
    }
}

Finally, the target endpoint is defined as follows in it's microservice:

package com.contractar.microserviciousuario.controllers;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import com.contractar.microserviciousuario.models.Cliente;
import com.contractar.microserviciousuario.models.Proveedor;
import com.contractar.microserviciousuario.models.Usuario;
import com.contractar.microserviciousuario.services.UsuarioService;
import com.contractar.serviciocommons.proveedores.ProveedorType;

import jakarta.validation.Valid;

@RestController
public class UsuarioController {
    @Autowired
    private UsuarioService usuarioService;

    @PostMapping("/usuarios")
    public ResponseEntity<Usuario> crearUsuario(@RequestBody @Valid Usuario usuario) {
        Usuario createdUsuario =  usuarioService.create(usuario); 
        return new ResponseEntity<Usuario>(createdUsuario, HttpStatus.CREATED);
    }

    @PostMapping("/usuarios/proveedor")
    public ResponseEntity<Proveedor> crearProveedor(@RequestBody @Valid Proveedor usuario,
         @RequestParam(required = true) ProveedorType proveedorType) {
        Proveedor createdUsuario =  usuarioService.createProveedor(usuario, proveedorType); 
        return new ResponseEntity<Proveedor>(createdUsuario, HttpStatus.CREATED);
    }

    @PostMapping("/usuarios/cliente")
    public ResponseEntity<Cliente> crearCliente(@RequestBody @Valid Cliente usuario) {
        Cliente createdUsuario =  usuarioService.createCliente(usuario); 
        return new ResponseEntity<Cliente>(createdUsuario, HttpStatus.CREATED);
    }
    
    @GetMapping("/usuarios")
    public ResponseEntity<Usuario> findByEmail(@RequestParam(required = true) String email) {
        Usuario usuario = usuarioService.findByEmail(email);
        return new ResponseEntity<Usuario>(usuario, HttpStatus.OK);
    }
}

I've checked zuul routing but didn't notice anything wrong. This was working well until I implemented the authorizarion layer.

0

There are 0 answers