Spring Data Rest和Cors

61

我正在开发一个使用Spring Boot和Rest接口的Dart前端应用程序。

XMLHttpRequest执行了一个OPTIONS请求,处理得非常正确。在此之后,发出最终的GET("/products")请求并失败:

所请求的资源上没有“Access-Control-Allow-Origin”标头。因此,来自 'http://localhost:63343' 的来源不被允许访问。

经过一些调试,我发现以下信息:
对于所有子类,AbstractHandlerMapping.corsConfiguration都会被填写,除了RepositoryRestHandlerMapping。
在RepositoryRestHandlerMapping中,创建时没有设置corsConfiguration,因此它不会被识别为cors路径/资源。
=> 没有附加CORS标头
这可能是问题吗?我该如何设置?

配置类:

@Configuration
public class RestConfiguration extends RepositoryRestMvcConfiguration {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**").allowCredentials(false).allowedOrigins("*").allowedMethods("PUT", "POST", "GET", "OPTIONS", "DELETE").exposedHeaders("Authorization", "Content-Type");
    }

   ...
}

我甚至尝试通过注释来设置跨域资源共享(Cors):

@CrossOrigin( methods = RequestMethod.GET, allowCredentials = "false")
public interface ProductRepository extends CrudRepository<Product, String> {


}

原始请求标头:

GET /products HTTP/1.1
Host: localhost:8080
Connection: keep-alive
Cache-Control: max-age=0
authorization: Basic dXNlcjpwYXNzd29yZA==
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/43.0.2357.130 Chrome/43.0.2357.130 Safari/537.36
Content-Type: application/json
Accept: */*
Referer: http://localhost:63343/inventory-web/web/index.html
Accept-Encoding: gzip, deflate, sdch
Accept-Language: de-DE,de;q=0.8,en-US;q=0.6,en;q=0.4

原始响应头:

HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Content-Type: application/hal+json;charset=UTF-8
Transfer-Encoding: chunked
Date: Thu, 30 Jul 2015 15:58:03 GMT

使用的版本: Spring Boot 1.3.0.M2 Spring 4.2.0.RC2

我错过了什么?


我不知道Spring Boot,但我已经在Java端使用过过滤器和Dart端的一些额外参数来完成它,可以参考以下链接查看Dart代码: https://dev59.com/T4nca4cB1Zd3GeqP9liU 以及 https://dev59.com/Zeo6XIcBkEYKwwoYJA7p同时,提供一些网络跟踪信息,这样可以更容易地查看实际发送和接收到的内容。 - Jan Vladimir Mostert
过滤器在我看来已经不再必要:https://stackoverflow.com/a/31539691/1309964 - 对于OPTIONS请求,这已经可以工作了。 - Thomas Letsch
听起来像是一个很棒的新注释!根据您的响应头,我期望看到以下标头,但实际上没有:Access-Control-Allow-Credentials:true/false. Access-Control-Allow-Methods:GET, POST, OPTIONS, DELETE Access-Control-Allow-Origin:http://____.com Access-Control-Max-Age:60 我假设Spring Boot会自动处理注释扫描,或者您手动设置了它?我假设这些注释位于某种控制器中,您是否有@Controller注释或某个告诉Spring扫描该类的注释? - Jan Vladimir Mostert
如果您直接将其放置在 @Controller / @RestController 的顶部,是否能像此示例中那样工作呢?https://spring.io/blog/2015/06/08/cors-support-in-spring-framework - Jan Vladimir Mostert
这个提交应该解决了问题: DATAREST-573 - 添加对新的CORS配置机制的支持...,它将包含在 2.6 版本中。 - Ortomala Lokni
5个回答

104

在 Spring Data REST 2.6 (Ingalls) 之前,只有由 Spring MVC 的 WebMvcConfigurationSupport 创建的 HandlerMapping 实例和带有 @CrossOrigin 注解的控制器才能识别 CORS。

但是现在 DATAREST-573 已经修复,RepositoryRestConfiguration 现在公开了一个全局设置的 getCorsRegistry() 方法,并且还可以识别存储库上的 @CrossOrigin 注解,因此这是推荐的方法。请参考 https://dev59.com/TVwZ5IYBdhLWcg3wM9_Z#42403956 中的具体示例。

对于那些必须坚持使用Spring Data REST 2.5(Hopper)或之前版本的人来说,我认为最好的解决方案是使用基于过滤器的方法。你可以使用Tomcat、Jetty或this one,但请注意,Spring Framework 4.2还提供了一个CorsFilter,它使用与@CrossOriginaddCorsMappings(CorsRegistry registry)方法相同的CORS处理逻辑。通过将一个UrlBasedCorsConfigurationSource实例传递给CorsFilter构造函数参数,你可以轻松获得像Spring本地CORS全局支持一样强大的功能。
如果你使用的是Spring Boot(支持Filter beans),可能会是这样的:
@Configuration
public class RestConfiguration {

    @Bean
    public FilterRegistrationBean corsFilter() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration config = new CorsConfiguration().applyPermitDefaultValues();
        source.registerCorsConfiguration("/**", config);
        FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));
        bean.setOrder(0);
        return bean;
    }
}

我修改了你的答案,以便大多数想要“复制”“默认”Spring Boot配置的人都可以获得一个合理的AllowCredentials=true配置,而不会像我一样沮丧地浪费数小时!:'( - Hendy Irawan
6
为了让这个方案与Spring-Security兼容,我必须使用第一种解决方案并像这样将CorsFilter添加到Spring-Security过滤器链中: http.addFilterBefore(corsFilter, ChannelProcessingFilter.class) - Kevin Wittek
3
由于某些原因,new CorsFilter(source)会抛出运行时异常,所以我最终手动创建了一个SimpleCorsFilter。但这个答案让我知道当一切看起来都正确连接时,我并不是疯了。 - adeady
4
我已经更新了我的 Spring + CORS 博客文章,添加了 FilterRegistrationBean。请注意,支持使用 config.addAllowedMethod("*") - Sébastien Deleuze
11
BeanInstantiationException:无法实例化 [javax.servlet.Filter]:工厂方法'springSecurityFilterChain'引发异常;嵌套的异常是org.springframework.beans.factory.BeanNotOfRequiredTypeException:期望名为 'corsFilter' 的Bean类型为'org.springframework.web.filter.CorsFilter',但实际上是'type org.springframework.boot.web.servlet.FilterRegistrationBean'。 - Dimitri Kopriwa
显示剩余5条评论

27

自从Ingalls列车已经实现,Spring Data中对CORS的支持现在已经启用。处理方式有两种:

  1. The @CrossOrigin annotation with specifying origins, methods, and allowedHeaders over a @RepositoryRestResource interface.

    @CrossOrigin(...)
    @RepositoryRestResource
    public interface PageRepository extends CrudRepository<Page, Long> { ... }
    
  2. A global configuration with the RepositoryRestConfiguration inside a @Configuration class. Marking repositories by the @CrossOrigin is not necessary then.

    @Configuration
    public class GlobalRepositoryRestConfigurer extends RepositoryRestConfigurerAdapter {
    
        @Override
        public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) {
            config.getCorsRegistry()
                      .addMapping(CORS_BASE_PATTERN)
                      .allowedOrigins(ALLOWED_ORIGINS)
                      .allowedHeaders(ALLOWED_HEADERS)
                      .allowedMethods(ALLOWED_METHODS);
         }
    
    }
    

1
第一种方法在使用JpaRepository时对我无效,但第二种方法有效。有什么想法是什么原因导致这种情况? - dvdgsng
@dvdgsng,我能看一下你尝试使用第一个选项的代码吗?这是一个房间http://chat.stackoverflow.com/rooms/142423 - Andrew Tobilko
1
谢谢!你从地狱般的一个bug中拯救了我的心理健康! - Cédric Levasseur
很遗憾,建议的两种解决方案都无法在Spring Boot 1.4.3和1.5.4 - Camden.SR4中使用。第一种方法没有任何区别,第二种方法在RepositoryRestConfiguration中找不到getCorsRegistry方法。 - Draško Kokić

9
由于升级了Spring Boot 1.5.2到1.5.6后,上面接受的答案中建议的方法对我来说不起作用。正如@BigDong的评论所指出的那样,我得到的异常是:
BeanInstantiationException: Failed to instantiate [javax.servlet.Filter]: Factory method 'springSecurityFilterChain' threw exception; nested exception is org.springframework.beans.factory.BeanNotOfRequiredTypeExcep‌​tion: Bean named 'corsFilter' is expected to be of type 'org.springframework.web.filter.CorsFilter' but was actually of type 'org.springframework.boot.web.servlet.FilterRegistrationBean
因此,这是我为了在我们的REST API的所有端点上获得“全局”CORS配置而想出来的方法,无论是使用Spring Data Rest还是Spring MVC实现的,所有端点都由Spring Security保护。
我无法将CorsFilter连接到正确的位置的请求管道中,因此我单独配置了SDR和MVC,但是通过这个帮助器使用相同的CorsRegistry配置它们。
public static void applyFullCorsAllowedPolicy(CorsRegistry registry) {
    registry.addMapping("/**") //
            .allowedOrigins("*") //
            .allowedMethods("OPTIONS", "HEAD", "GET", "PUT", "POST", "DELETE", "PATCH") //
            .allowedHeaders("*") //
            .exposedHeaders("WWW-Authenticate") //
            .allowCredentials(true)
            .maxAge(TimeUnit.DAYS.toSeconds(1));
}

然后针对MVC:
@Configuration
@EnableWebSecurity(debug = true)
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class CustomWebSecurityConfiguration extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // enables CORS as per
        // https://docs.spring.io/spring-security/site/docs/current/reference/html/cors.html#cors
        http.cors()
            .and() // ...
    }

    @Bean
    public WebMvcConfigurer corsConfigurer() {
        return new WebMvcConfigurerAdapter() {
            @Override
            public void addCorsMappings(CorsRegistry registry) {
                applyFullCorsAllowedPolicy(registry);
            }
        };
    }
}

然后对于SDR:
public class CustomRepositoryRestMvcConfiguration extends RepositoryRestConfigurerAdapter {

@Override
public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) {
    config.setReturnBodyOnCreate(true);
    config.setReturnBodyForPutAndPost(true);
    config.setReturnBodyOnUpdate(true);
    config.setMaxPageSize(250);
    config.setDefaultPageSize(50);
    config.setDefaultMediaType(MediaTypes.HAL_JSON);
    config.useHalAsDefaultJsonMediaType(true);

    CustomWebSecurityConfiguration.applyFullCorsAllowedPolicy(config.getCorsRegistry());
}

以下是帮助我得出这个答案的相关主题的参考资料:

8

RepositoryRestConfigurerAdapter从3.1版本开始已被弃用,因此如果您使用的是Spring Boot和Data-Rest 3.1以上版本,则可以直接实现RepositoryRestConfigurer

@Configuration
public class ConfigRepositoryRest implements RepositoryRestConfigurer {
@Override
public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) {
    config.getCorsRegistry()
            .addMapping("/**")
            .allowedOrigins("http://localhost:3000");
}

}


感激不尽!!! 这个解决方案适用于2.3.4.RELEASE和reactjs(axios)。那么如何在Spring Boot教程/文档中找到这样的解决方案?@CrossOrigina注释似乎无法与数据存储库一起使用。我花了两天时间寻找解决方案,这就是答案。我错过了什么? - Radoslav Lang
这个方法可行!我之前尝试过类似的方法,使用了 @Bean public WebMvcConfigurer corsConfigurer() { 但是没有成功。 - Șerban Ghiță
1
仅仅是为了补充上面的内容,这是根据文档 https://docs.spring.io/spring-data/rest/docs/current/reference/html/#reference 推荐的方式,在章节“Customizing Spring Data REST”下的“Configuring CORS”部分进行配置。 - garfield

0
我正在尝试从Angular调用Spring REST服务。 Spring REST项目部署在Tomcat服务器上,而Angular是默认的Angular服务器。 当从Angular调用服务时,我遇到了这个问题。 我尝试着去跟进。

https://juristr.com/blog/2016/11/configure-proxy-api-angular-cli/

但问题仍然存在。 感谢我的前辈“阿巴斯兄弟”,他建议在Spring配置文件中添加一些配置以避免这个问题,所以我在Spring配置中添加了这段代码。

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

@Configuration
@EnableWebMvc
@ComponentScan("org.liferayasif.backend")
public class RestConfig extends WebMvcConfigurerAdapter{

    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer){
        configurer.enable();
    }

    /*
     * (non-Javadoc)
     * @see org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter#addCorsMappings(org.springframework.web.servlet.config.annotation.CorsRegistry)
     * To avoid 'Access-Control-Allow-Origin'
     * Above error arising when I am hitting from angular to our rest service
     */
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**");
    }

}

这个方法解决了我的“Access-Control-Allow-Origin”跨域问题。

@Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**");
    }

可以下载我的整个项目以供参考。
我的Github链接:

https://github.com/asifaftab87/SpringPersistenceHibernate

分支 - 安全

项目 - 模型


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接