Spring Boot Data Rest + CORS 未正确启用 OPTIONS/DELETE

Posted

技术标签:

【中文标题】Spring Boot Data Rest + CORS 未正确启用 OPTIONS/DELETE【英文标题】:Spring Boot Data Rest + CORS not being enabled properly for OPTIONS/DELETE 【发布时间】:2017-02-21 21:52:55 【问题描述】:

我有一个非常简单的例子,我无法开始工作。

我有我的域来模拟我的数据库和我的存储库。

public interface MyTestRepository extends CrudRepository<MyTest, Integer> 

我用http://resttesttest.com/ 来测试它。对于 GET 方法,它会毫无问题地返回我的 JSON REST 信息。

我可以查询端点http://localhost:8080/mytest/1 并从数据库中取回 id=1 的信息。

但是,当我尝试使用 DELETE 选项时,问题就出现了。如果我在 http://localhost:8080/mytest/1 上运行 DELETE,我会得到 ​​p>

对预检请求的响应未通过访问控制检查:否 请求中存在“Access-Control-Allow-Origin”标头 资源。因此不允许使用原点“http://resttesttest.com” 使用权。响应的 HTTP 状态代码为 403。

我最初尝试了以下,但发现我无法使用它,因为我使用的是 Spring-data-Rest。 https://jira.spring.io/browse/DATAREST-573

@Override
public void addCorsMappings(CorsRegistry registry) 
    registry.addMapping("/**")
        .allowedOrigins("*")
        .allowedMethods("*")
        .allowedHeaders("*")
        .allowCredentials(true).maxAge(3600);

我搜索了一下,发现了这个。

How to configure CORS in a Spring Boot + Spring Security application?

所以我加了

@Bean
public FilterRegistrationBean corsFilter() 
    UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    CorsConfiguration config = new CorsConfiguration();
    config.setAllowCredentials(true);
    config.addAllowedOrigin("*");
    config.addAllowedHeader("*");
    config.addAllowedMethod("*");
    source.registerCorsConfiguration("/**", config);
    FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));
    bean.setOrder(0);
    return bean;

我也找到了这个帖子。

Spring Data Rest and Cors

也尝试了以下代码,但没有成功。

@Bean
public FilterRegistrationBean 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");
    source.registerCorsConfiguration("/**", config);
    // return new CorsFilter(source);
    final FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));
    bean.setOrder(0);
    return bean;

我添加了一个 catch all 来测试,它应该允许所有 CORS 明智地通过,但是即使我有“*”,我仍然会不断收到 No 'Access-Control-Allow-Origin'。

此时,我不知道为什么预检请求未通过访问控制检查我缺少什么。

curl 发出删除没有问题。

编辑:

最终找到了确切的解决方案。我不确定我所拥有的和这种方法之间的区别,但这似乎有效。

import org.springframework.stereotype.Component;

import javax.servlet.*;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * Note this is a very simple CORS filter that is wide open.
 * This would need to be locked down.
 * Source: https://***.com/questions/39565438/no-access-control-allow-origin-error-with-spring-restful-hosted-in-pivotal-web
 */
@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", "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() 


【问题讨论】:

在不使用 spring-boot-starter-security 的情况下直接使用 Spring Boot MVC/Rest 时,我必须使用上面的 CORSFIlter 来允许预检 OPTIONS 工作。当我使用 Postman 发送带有 Origin 标头的 OPTIONS 请求时,如果没有过滤器,我会收到 403 Forbidden(如果我关闭 Origin,OPTIONS 会正常工作)。我需要“公共类 CORSFilter 实现过滤器”和“公共类 CORSconfiguration 实现 WebMvcConfigurer”才能使 OPTIONS 正常工作 【参考方案1】:

以下配置适用于基于 Spring Data Rest 的应用程序。需要注意的重要一点是,过滤器已注册为在安全过滤器链启动之前执行。

@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);
  

【讨论】:

谢谢,但这似乎也不起作用。我将其复制并直接粘贴到我的 Spring 应用程序中,禁用了所有其他添加导入的 @Configuration 类,但仍然出现相同的错误。您在使用 Springs Boot Data Rest 吗?如果是这样,您是否使用自动生成 REST 端点的 Repository 类?【参考方案2】:

这是我用作允许所有 CORS servlet 过滤器的内容:

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
    

【讨论】:

谢谢,但是基于我的 OP 中的那个错误,因为我使用的是 Spring Data REST,所以它对我不起作用 好的,这很有趣.. 我们使用的是 spring mvc + spring data,当从我们的 Angular Web 客户端调用时,该过滤器似乎工作正常 您在使用自动生成的存储库吗?例如存储库()?我用@CrossOrigin(origins = "*") 标记来测试的类似乎没有这个问题。 好的,我已经纠正了。我尝试使用您的代码,但似乎没有用。我最终找到了这个***.com/a/39573047/3314194,将该代码复制并粘贴到一个类中,它就可以工作了。这和你的概念是一样的。我会将您的答案标记为答案,但我将对我的问题添加一个编辑,这是对我有用的完整代码(随时将其添加到您的答案中)。不过,我仍然不明白 3 种不同的 CORS 过滤器之间的区别。 谢谢 :) 是的,它看起来确实很相似,它可能与需要明确允许删除的“访问控制允许方法”有关【参考方案3】:

使用 Spring Boot 2.2.6

我必须添加一个过滤器以允许 OPTIONS 工作。没有它,我得到了 403 Forbidden。 “Origin”请求标头是触发 403 的原因 - 我在 Postman 中进行了测试,并且没有发送该标头 OPTIONS 在没有过滤器的情况下工作。

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() 
        
        

随着

@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);
    

【讨论】:

【参考方案4】:

我似乎有同样的问题。 CrossOrigin 配置适用于 GET/PUT/POST,但是当我为 Spring PostMapping 方法请求 OPTIONS 时,响应会省略 Access-Control-Allow-Methods 标头:

@CrossOrigin
public class ArticleController 

@DeleteMapping("/uuid")
public void delete(@PathVariable String uuid) throws ArticleNotFoundException 
    articleService.delete(uuid);

如果我 curl 进行 DELETE,我会得到一个 HTTP 200,包括 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

如果我为 OPTIONS 卷曲,我会得到 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

我错过了什么吗?

编辑 1:

如果我将此映射添加到控制器(基于 Enable CORS for OPTIONS request using Spring Framework ):

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

这会导致:

$ 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

但它并没有解决 Angular 的问题,它仍然给出 403

编辑 2: 我已经能够使用以下控制器代码解决此问题:

@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);

【讨论】:

以上是关于Spring Boot Data Rest + CORS 未正确启用 OPTIONS/DELETE的主要内容,如果未能解决你的问题,请参考以下文章

在Spring Boot Data JPA Rest API中使用PostgreSql时,表未找到错误

Spring Boot + Spring Data Rest Repositories 中使用 @CrossOrigin 注释的 CORS 问题

在 Spring Boot Data REST 中进行实体验证后运行 @HandleBeforeCreate

无法在 Spring Boot Data Rest 中启用 CORS

Spring boot 2.0.3.RELEASE, Spring data rest, application error, failed to start

Spring Boot MongoDB REST - 自定义存储库方法