Spring Boot Data Rest + CORS not being enabled properly for OPTIONS/DELETE

后端 未结 4 1004
暖寄归人
暖寄归人 2020-12-21 07:08

I\'ve got an extremely simple example that I can\'t get to work.

I have my domain that models my database, and my Repository.

public interface MyTes         


        
相关标签:
4条回答
  • 2020-12-21 07:21

    Using Spring Boot 2.2.6

    I had to add a filter to allow OPTIONS to work. Without it, I got a 403 Forbidden. The "Origin" request header is what triggered the 403 - I tested in Postman and without sending that header OPTIONS worked without a filter.

    import javax.servlet.*;
    import javax.servlet.http.HttpServletResponse;
    @Component
                
            public class CORSFilter implements Filter {
                    
                    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
                        HttpServletResponse response = (HttpServletResponse) res;
                        response.setHeader("Access-Control-Allow-Origin", "*");
                        response.setHeader("Access-Control-Allow-Methods", "OPTIONS");  // "POST, GET, PUT, OPTIONS, DELETE"
                        response.setHeader("Access-Control-Max-Age", "3600");
                        response.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
                        chain.doFilter(req, res);
                    }
            
                public void init(FilterConfig filterConfig) {}
            
                public void destroy() {}
            
            }
    

    Along with

    @Configuration
    public class ConfigCORS implements WebMvcConfigurer {
    @Override
        public void addCorsMappings(CorsRegistry registry) {
    
            registry.addMapping("/**")  
                    .allowedOrigins("*")  allowedOrigins("http://localhost:3000")
                    .allowedMethods("POST", "PUT", "GET",  "DELETE", "OPTIONS") 
                    .allowedHeaders("Content-Type", "Origin")
                    .exposedHeaders("X-Total-Count", "Location", "Access-Control-Allow-Origin")  
                    .allowCredentials(false)
                    .maxAge(6000);
        }
    }
    
    0 讨论(0)
  • 2020-12-21 07:34

    This is what I use as a permit all CORS servlet filter:

    public class PermissiveCORSFilter implements Filter {
    
        private static final Logger LOGGER = LoggerFactory.getLogger(PermissiveCORSFilter.class);
        private static final Pattern PATTERN = Pattern.compile("^[a-zA-Z0-9 ,-_]*$");
    
        @Override
        public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
            HttpServletResponse response = (HttpServletResponse) res;
            HttpServletRequest request = (HttpServletRequest) req;
    
            String origin;
            String credentialFlag;
            if (request.getHeader("Origin") == null) {
                origin = "*";
                credentialFlag = "false";
             } else {
                origin = request.getHeader("Origin");
                credentialFlag = "true";
             }
    
            // need to do origin.toString() to avoid findbugs error about response splitting        
            response.addHeader("Access-Control-Allow-Origin", origin.toString());
            response.setHeader("Access-Control-Allow-Credentials", credentialFlag);
            if ("OPTIONS".equals(request.getMethod())) {
                LOGGER.info("Received OPTIONS request from origin:" + request.getHeader("Origin"));
                response.setHeader("Access-Control-Allow-Methods", "GET,POST,HEAD,OPTIONS,PUT,DELETE");
                response.setHeader("Access-Control-Max-Age", "3600");
                String headers = StringUtils.trimToEmpty(request.getHeader("Access-Control-Request-Headers"));
                if (!PATTERN.matcher(headers).matches()) {
                    throw new ServletException("Invalid value provided for 'Access-Control-Request-Headers' header");
                }
                response.setHeader("Access-Control-Allow-Headers", headers); // allow any headers
            }
            chain.doFilter(req, res);
        }
    
        @Override
        public void init(FilterConfig filterConfig) {
            // Do nothing
        }
    
        @Override
        public void destroy() {
            // Do nothing
        }
    
    0 讨论(0)
  • 2020-12-21 07:34

    The following configuration works for me in a Spring Data Rest based application. The important point to note is that the filter is registered to execute before the Security Filter chain kicks in.

    @Configuration
    @EnableWebSecurity
    public class SecurityConfiguration extends WebSecurityConfigurerAdapter
    {
      @Override
      public void configure(HttpSecurity http) throws Exception
      {
        http.addFilterBefore(corsFilter(), ChannelProcessingFilter.class);
      }
    
      @Bean
      protected Filter corsFilter()
      {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    
        CorsConfiguration config = new CorsConfiguration();
        config.setAllowCredentials(true);
        config.addAllowedOrigin("*");
        config.addAllowedHeader("*");
        config.addAllowedMethod("OPTIONS");
        config.addAllowedMethod("HEAD");
        config.addAllowedMethod("GET");
        config.addAllowedMethod("PUT");
        config.addAllowedMethod("POST");
        config.addAllowedMethod("DELETE");
        config.addAllowedMethod("PATCH");
        config.addExposedHeader("Location");
    
        source.registerCorsConfiguration("/**", config);
    
        return new CorsFilter(source);
      }
    }
    
    0 讨论(0)
  • 2020-12-21 07:37

    I seem to have the same issue. CrossOrigin config works fine with GET/PUT/POST, but when I request OPTIONS for my Spring PostMapping method the response omits the Access-Control-Allow-Methods header:

    @CrossOrigin
    public class ArticleController {
    
    @DeleteMapping("/{uuid}")
    public void delete(@PathVariable String uuid) throws ArticleNotFoundException {
        articleService.delete(uuid);
    }
    

    If I curl for DELETE, I get a HTTP 200 including Access-Control-Allow-Methods:

    $ curl -v -H "Access-Control-Request-Method: DELETE" -H "Origin: http://localhost:4200" -X OPTIONS http://localhost:8080/article/someuuid
    < HTTP/1.1 200
    < Access-Control-Allow-Origin: http://localhost:4200
    < Access-Control-Allow-Methods: PUT,POST,GET,DELETE,OPTIONS
    < Allow: GET, HEAD, POST, PUT, DELETE, OPTIONS, PATCH
    

    If I curl for OPTIONS, I get a 403:

    $ curl -v -H "Access-Control-Request-Method: OPTIONS" -H "Origin: http://localhost:4200" -X OPTIONS http://localhost:8080/article/someuuid
    < HTTP/1.1 403
    

    Am I missing something here?

    EDIT 1:

    If I add this mapping to the controller (based on Enable CORS for OPTIONS request using Spring Framework ):

    @RequestMapping(
            value = "/**",
            method = RequestMethod.OPTIONS
    )
    public ResponseEntity handle() {
        return new ResponseEntity(HttpStatus.OK);
    }
    

    This results in:

    $ curl -v -H "Access-Control-Request-Method: OPTIONS" -H "Origin: http://localhost:4200" -X OPTIONS http://localhost:8080/article/someuuid
    < HTTP/1.1 200
    < Access-Control-Allow-Origin: *
    < Access-Control-Allow-Methods: OPTIONS
    < Allow: GET, HEAD, POST, PUT, DELETE, OPTIONS, PATCH
    

    But it doesn't solve the issue for Angular which still gives a 403

    EDIT 2: I've been able to fix this with the following Controller-code:

    @RequestMapping("/article")
    @CrossOrigin(origins="http://localhost:4200",
        methods = {RequestMethod.PUT, RequestMethod.POST, RequestMethod.GET, RequestMethod.DELETE, RequestMethod.OPTIONS}
        )
    public class ArticleController {
    
    @RequestMapping(
            value = "/{uuid}",
            method = { RequestMethod.DELETE })
    public void delete(@PathVariable String uuid) throws ArticleNotFoundException {
        articleService.delete(uuid);
    }
    
    @RequestMapping(method = { RequestMethod.OPTIONS})
    public ResponseEntity handle() {
        return new ResponseEntity(HttpStatus.OK);
    }
    
    0 讨论(0)
提交回复
热议问题