javaspring-bootmicroservicesspring-cloudnetflix-eureka

Why does calling my microservice in Java Spring Boot returns 403 Forbidden?


I am using SpringBoot with Eureka Server. I have a users microservice, one eureka server and one springcloud apigateway folder. Here is the complete source code

In my users microservice's controller , I have below code

@RestController
@RequestMapping("/users")
public class UsersController {

    @Autowired
    private Environment env;

    @Autowired
    UsersService usersService;

    @GetMapping("/status/check")
    public String status(){
        System.out.println("hereeeeeeeeeee");
        return "Working "+env.getProperty("local.server.port");
    }

    @PostMapping
    public ResponseEntity<CreateUserResponseModel> createUser(@Valid @RequestBody CreateUserRequestModel userDetails) {
        ModelMapper modelMapper = new ModelMapper();
        modelMapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT);
        UserDto userDto = modelMapper.map(userDetails, UserDto.class);
        UserDto createdUser = usersService.createUser(userDto);
        CreateUserResponseModel body = modelMapper.map(createdUser, CreateUserResponseModel.class);
        return  ResponseEntity.status(HttpStatus.CREATED).body(body);
    }
    
}

Now the Get api endpoint returns in 403 Forbidden , the post endpoint works fine

Below is the screenshot of the error

enter image description here

I have defined AuthorizationHeaderFilter class in apigateway which is used for decoding jwt which I am sending in the GET Api Endpoint and it properly decodes the JWT

@Component
public class AuthorizationHeaderFilter extends AbstractGatewayFilterFactory<AuthorizationHeaderFilter.Config>{

    @Autowired
    private Environment environment;

    public AuthorizationHeaderFilter(){
        super(Config.class);
    }

    public static class Config{

    }

    @Override
    public GatewayFilter apply(Config config) {
        return (exchange,chain)->{
            ServerHttpRequest request = exchange.getRequest();
            if(!request.getHeaders().containsKey(HttpHeaders.AUTHORIZATION)){
                return onError(exchange,"No authorization header",HttpStatus.UNAUTHORIZED);
            }
            String authorizationHeader = request.getHeaders().get(HttpHeaders.AUTHORIZATION).get(0);
            String jwt = authorizationHeader.replace("Bearer ", ""); //Make sure you add space
            if(!isJwtValid(jwt)){
                return onError(exchange, "Jwt token is invalid",HttpStatus.UNAUTHORIZED);
            }
            return chain.filter(exchange);
        };
    }

    private Mono<Void> onError(ServerWebExchange exchange,String err,HttpStatus httpStatus){
        ServerHttpResponse response = exchange.getResponse();
        response.setStatusCode(httpStatus);
        return response.setComplete();
    }

    private boolean isJwtValid(String jwt){
        System.out.println("1");
        boolean isValid = false;
        String subject = null;
        String tokenSecret = environment.getProperty("token.secret");
        System.out.println("2 "+tokenSecret);
        SecretKey secretKey = Keys.hmacShaKeyFor(tokenSecret.getBytes());

        JwtParser jwtParser = Jwts.parser().verifyWith(secretKey).build();
        try{
            // Jwt<Header,Claims> parsedToken = jwtParser.parse(jwt);
            subject = jwtParser.parseSignedClaims(jwt).getPayload().get("userId").toString();
            System.out.println("3 "+subject);
        } catch(Exception ex){
            isValid = false;
            System.out.println("4 "+ex.getLocalizedMessage());
        }

        if(subject == null || subject.isEmpty()){
            isValid = false;
        } else {
            isValid = true;
        }
        System.out.println("5 ");
        return isValid;
    }

  
}

Here is the application.properties of my apigateway

spring.application.name=api-gateway
server.port=8082
eureka.client.service-url.defaultZone=http://localhost:8010/eureka

spring.cloud.gateway.routes[0].id=users-status-check
spring.cloud.gateway.routes[0].uri=lb://users-ws
spring.cloud.gateway.routes[0].predicates[0]=Path=/users-ws/users/status/check
spring.cloud.gateway.routes[0].predicates[1]=Method=GET
spring.cloud.gateway.routes[0].predicates[2]=Header=Authorization, Bearer (.*)
spring.cloud.gateway.routes[0].filters[0]=RemoveRequestHeader=Cookie
spring.cloud.gateway.routes[0].filters[1]=RewritePath=/users-ws/(?<segment>.*), /$\{segment}
spring.cloud.gateway.routes[0].filters[2]=AuthorizationHeaderFilter

spring.cloud.gateway.routes[1].id=users-ws
spring.cloud.gateway.routes[1].uri=lb://users-ws
spring.cloud.gateway.routes[1].predicates[0]=Path=/users-ws/users
spring.cloud.gateway.routes[1].predicates[1]=Method=POST
spring.cloud.gateway.routes[1].filters[0]=RemoveRequestHeader=Cookie
spring.cloud.gateway.routes[1].filters[1]=RewritePath=/users-ws/(?<segment>.*), /$\{segment}

spring.cloud.gateway.routes[2].id=users-ws-h2-console
spring.cloud.gateway.routes[2].uri=lb://users-ws
spring.cloud.gateway.routes[2].predicates[0]=Path=/users-ws/h2-console
spring.cloud.gateway.routes[2].predicates[1]=Method=GET
spring.cloud.gateway.routes[2].filters[0]=RemoveRequestHeader=Cookie
spring.cloud.gateway.routes[2].filters[1]=RewritePath=/users-ws/(?<segment>.*), /$\{segment}

spring.cloud.gateway.routes[3].id=users-ws-login
spring.cloud.gateway.routes[3].uri=lb://users-ws
spring.cloud.gateway.routes[3].predicates[0]=Path=/users-ws/users/login
spring.cloud.gateway.routes[3].predicates[1]=Method=POST
spring.cloud.gateway.routes[3].filters[0]=RemoveRequestHeader=Cookie
spring.cloud.gateway.routes[3].filters[1]=RewritePath=/users-ws/(?<segment>.*), /$\{segment}

spring.cloud.gateway.routes[4].id=users-ws-get-update-delete
spring.cloud.gateway.routes[4].uri=lb://users-ws
spring.cloud.gateway.routes[4].predicates[0]=Path=/users-ws/users/**
spring.cloud.gateway.routes[4].predicates[1]=Method=GET,PUT,DELETE
spring.cloud.gateway.routes[4].predicates[2]=Header=Authorization, Bearer (.*)
spring.cloud.gateway.routes[4].filters[0]=RemoveRequestHeader=Cookie
spring.cloud.gateway.routes[4].filters[1]=AuthorizationHeaderFilter
spring.cloud.gateway.routes[4].filters[2]=RewritePath=/users-ws/(?<segment>.*), /$\{segment}

token.secret=1aBcD3eF4gH5iJ6kL7mN8oP9qRsTuVwXyZ0A1bCdEfG2hI3jK4lM5nO6pQ7rStU8vW9xYz

Solution

  • I updated the file WebSecurity.java in your users service, and changed the line:

    .authorizeHttpRequests(auth -> auth.requestMatchers(HttpMethod.POST, "/users")
    .permitAll()
    

    to

    .authorizeHttpRequests(auth -> auth.requestMatchers("/users/**")
    .permitAll()
    

    So that Spring Security allows not only POST requests but also the GET ones and also you are able to call paths under /users. After that I got a correct response.