Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Springboot endpoint 403 OPTIONS when doing a POST request

I'm running a service using Spring and my Angular front-end is getting a 403 with Request Method: OPTIONS when it tries to make a POST request.

Both the Spring service and the Angular app are running locally on my machine. I tried toggling CORS with a Chrome plugin, but that didn't seem to fix the issue.

All my GET requests to the service seem to work alright. I can do the POST request in Postman, so I'm not sure why the angular app can't make the request, but Postman can.

****EDIT****

Response Headers

Allow: GET, HEAD, POST, PUT, DELETE, OPTIONS, PATCH
Content-Length: 20
Date: Sat, 31 Mar 2018 19:15:01 GMT

Request Headers

Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Access-Control-Request-Headers: content-type
Access-Control-Request-Method: POST
Connection: keep-alive
Host: localhost:9901
Origin: http://localhost:4200
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.162 Safari/537.36
like image 498
Nickknack Avatar asked Mar 24 '18 23:03

Nickknack


People also ask

How do I fix 403 Forbidden in Postman?

The simple answer is; “You need to be given the correct access”. Without being given the correct access you'd technically be hacking the server, as it is specifically set up to restrict said access.

How do you use spring boot security?

For adding a Spring Boot Security to your Spring Boot application, we need to add the Spring Boot Starter Security dependency in our build configuration file. Maven users can add the following dependency in the pom. xml file. Gradle users can add the following dependency in the build.


3 Answers

To fix all CORS issues in an Angular(front end) plus Spring boot(backend) project, add the following Spring component:

import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class ConfigCtrl implements Filter {
    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
        final HttpServletResponse response = (HttpServletResponse) res;
        response.setHeader("Access-Control-Allow-Origin", "*");
        response.setHeader("Access-Control-Allow-Methods", "POST, PUT, GET, OPTIONS, DELETE");
        response.setHeader("Access-Control-Allow-Headers", "Authorization, Content-Type");
        response.setHeader("Access-Control-Max-Age", "3600");
        if ("OPTIONS".equalsIgnoreCase(((HttpServletRequest) req).getMethod())) {
            response.setStatus(HttpServletResponse.SC_OK);
        } else {
            chain.doFilter(req, res);
        }
    }
    @Override
        public void destroy() {
    }
    @Override
        public void init(FilterConfig config) throws ServletException {
    }
}

PS for beginners: The name of the class and its location within the Spring app does not matter.
Credit to Ajitesh Kumar.

like image 91
Abubakar Ahmad Avatar answered Oct 21 '22 14:10

Abubakar Ahmad


CORS Request is made by your Frontend to see what are the methods (HTTP Verbs) that your backed allows. This is usually required for monetary operations e.g., POST or PUT which are meant to modify data.

Hence your Frontend will make this call first and your backend needs to respond with allowed methods, you can also restrict specific URIs, then upon successful validation, the target call is made.

This is perfectly normal and angular does this internally so as to not make an unnecessary data request without knowing whether the server will allow.

Here's how you will set it up in Spring.

    //Change/Customize as necessary
    @Bean
    CorsConfigurationSource corsConfigurationSource() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration corsConfiguration = new CorsConfiguration();
        corsConfiguration.addAllowedOrigin("<your origin>");
        corsConfiguration.setAllowedMethods(Arrays.asList(
                HttpMethod.GET.name(),
                HttpMethod.HEAD.name(),
                HttpMethod.POST.name(),
                HttpMethod.PUT.name(),
                HttpMethod.DELETE.name()));
        corsConfiguration.setMaxAge(1800L);
        source.registerCorsConfiguration("/**", corsConfiguration); // you restrict your path here
        return source;
    }

If you are also using any custom response headers from your backend, then you need to allow that as well in the CORS configuration. As an example

    corsConfiguration.addAllowedHeader("*");
    corsConfiguration.addExposedHeader("header1");
    corsConfiguration.addExposedHeader("header2");
like image 45
bitscanbyte Avatar answered Oct 21 '22 13:10

bitscanbyte


If we are talking about SpringBoot, presumably latest or at least recent version, then we can simply use the @CrossOrigin annotation next to the @RestController annotation in our controller classes. It is available since Spring ver. 4.2

For example:

@RestController
@CrossOrigin
@RequestMapping("/api")
public class MyObjectsController {
    private final MyObjectsService service;

    @Autowired
    public MyObjectsController(MyObjectsService service) {
        this.service = service;
    }

    @GetMapping("/version")
    public Version getVersion() {
        return service.getVersion();
    }

    @PostMapping("/objects")
    public ObjectResource createObject(@RequestBody @Valid ObjectData data) {
        return service.createObject(data);
    }

    @GetMapping("/objects/{id}")
    public ObjectResource getObject(@PathVariable String id) {
        return service.getObjectById(id);
    }
}

Benefits:

  • annotated controller behaves a way better (and smarter) than any kind of self-written filters
  • it is also more flexible than a fixed CORS configuration for the whole project, since you can control what part of your API should support CORS headers and what should be only available for server-to-server communication
  • only methods supported by controller will be declared and allowed in the response to OPTIONS request
  • CORS headers will only be present in the response to CORS requests (i.e. presence of Referer header is analyzed)
  • etc.

See also:

  • Enabling Cross Origin Requests for a RESTful Web Service
  • CrossOrigin Annotation javadoc
like image 40
Vladimir L. Avatar answered Oct 21 '22 12:10

Vladimir L.