如何在Spring Boot + Spring Security应用程序中配置CORS?

Rud*_*idt 46 javascript spring spring-security cors spring-boot

我使用Spring Boot和Spring Security以及Cors支持.

如果我执行以下代码

url = 'http://localhost:5000/api/token'
xmlhttp = new XMLHttpRequest
xmlhttp.onreadystatechange = ->
    if xmlhttp.readyState is 4
        console.log xmlhttp.status
xmlhttp.open "GET", url, true
# xmlhttp.setRequestHeader "X-Requested-With", "XMLHttpRequest"
xmlhttp.setRequestHeader 'Authorization', 'Basic ' + btoa 'a:a'
do xmlhttp.send
Run Code Online (Sandbox Code Playgroud)

我得到了结果

200
Run Code Online (Sandbox Code Playgroud)

如果我测试错误的凭据,如

url = 'http://localhost:5000/api/token'
xmlhttp = new XMLHttpRequest
xmlhttp.onreadystatechange = ->
    if xmlhttp.readyState is 4
        console.log xmlhttp.status
xmlhttp.open "GET", url, true
# xmlhttp.setRequestHeader "X-Requested-With", "XMLHttpRequest"
xmlhttp.setRequestHeader 'Authorization', 'Basic ' + btoa 'a:aa'
do xmlhttp.send
Run Code Online (Sandbox Code Playgroud)

而不是获得401(这是春季安全中错误身份验证的标准代码),我得到了

0
Run Code Online (Sandbox Code Playgroud)

以下浏览器通知:

获取http:// localhost:5000/api/token

XMLHttpRequest无法加载http:// localhost:5000.请求的资源上不存在"Access-Control-Allow-Origin"标头.因此不允许来源' http:// localhost:3000 '访问.响应具有HTTP状态代码401.

我正在开发前端代码,需要服务器响应中的有用http状态代码来处理这种情况.我需要比0更有用的东西.响应体也是空的.我不知道我的配置是否错误,或者它是一个软件错误,我也不知道在哪里,如果它是铬(使用arch linux)或spring security.

我的Spring配置是:

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

@RestController
@RequestMapping("api")
public class Controller {
    @RequestMapping("token")
    @CrossOrigin
    Map<String, String> token(HttpSession session) {
        return Collections.singletonMap("token", session.getId());
    }
}

@EnableWebSecurity
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication().withUser("a").password("a").roles("USER");
    }
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests()
                .requestMatchers(CorsUtils::isPreFlightRequest).permitAll()
                .anyRequest().authenticated()
                .and().httpBasic();
    }
}
Run Code Online (Sandbox Code Playgroud)

如果我用curl测试一切都很完美,我认为因为不需要CORS支持,但我尝试用OPTION请求模拟CORS,结果也没问题.

$ curl -v localhost:5000/api/token -H "Authorization: Basic YTpha"
*   Trying ::1...
* Connected to localhost (::1) port 5000 (#0)
> GET /api/token HTTP/1.1
> Host: localhost:5000
> User-Agent: curl/7.48.0
> Accept: */*
> Authorization: Basic YTpha
> 
< HTTP/1.1 200 OK
< Server: Apache-Coyote/1.1
< X-Content-Type-Options: nosniff
< X-XSS-Protection: 1; mode=block
< Cache-Control: no-cache, no-store, max-age=0, must-revalidate
< Pragma: no-cache
< Expires: 0
< X-Frame-Options: DENY
< Access-Control-Allow-Origin: http://localhost:3000
< Access-Control-Allow-Methods: POST,GET,OPTIONS,DELETE
< Access-Control-Max-Age: 3600
< Access-Control-Allow-Credentials: true
< Access-Control-Allow-Headers: Origin,Accept,X-Requested-    With,Content-Type,Access-Control-Request-Method,Access-Control-Request-Headers,Authorization
< x-auth-token: 58e4cca9-7719-46c8-9180-2fc16aec8dff
< Content-Type: application/json;charset=UTF-8
< Transfer-Encoding: chunked
< Date: Sun, 01 May 2016 16:15:44 GMT
< 
* Connection #0 to host localhost left intact
{"token":"58e4cca9-7719-46c8-9180-2fc16aec8dff"}
Run Code Online (Sandbox Code Playgroud)

并使用错误的凭据:

$ curl -v localhost:5000/api/token -H "Authorization: Basic YTp"
*   Trying ::1...
* Connected to localhost (::1) port 5000 (#0)
> GET /api/token HTTP/1.1
> Host: localhost:5000
> User-Agent: curl/7.48.0
> Accept: */*
> Authorization: Basic YTp
> 
< HTTP/1.1 401 Unauthorized
< Server: Apache-Coyote/1.1
< X-Content-Type-Options: nosniff
< X-XSS-Protection: 1; mode=block
< Cache-Control: no-cache, no-store, max-age=0, must-revalidate
< Pragma: no-cache
< Expires: 0
< X-Frame-Options: DENY
< WWW-Authenticate: Basic realm="Realm"
< Content-Type: application/json;charset=UTF-8
< Transfer-Encoding: chunked
< Date: Sun, 01 May 2016 16:16:15 GMT
< 
* Connection #0 to host localhost left intact
{"timestamp":1462119375041,"status":401,"error":"Unauthorized","message":"Failed to decode basic authentication token","path":"/api/token"}
Run Code Online (Sandbox Code Playgroud)

编辑:避免误解.我使用1.3.3 Spring Boot.博客文章写道:

CORS支持将在即将发布的Spring Boot 1.3版本中提供,并已在1.3.0.BUILD-SNAPSHOT版本中提供.

在Spring Boot应用程序中使用带有@CrossOrigin注释的控制器方法CORS配置不需要任何特定配置.

可以通过使用自定义的addCorsMappings(CorsRegistry)方法注册WebMvcConfigurer bean来定义全局CORS配置:

我添加了以下代码以启用全局cors支持.实际上我之前尝试了这个,但结果是一样的.我最近再次尝试过,结果是一样的.

@Configuration
public class MyConfiguration {

    @Bean
    public WebMvcConfigurer corsConfigurer() {
        return new WebMvcConfigurerAdapter() {
            @Override
            public void addCorsMappings(CorsRegistry registry) {
                registry.addMapping("/**");
            }
        };
    }
}
Run Code Online (Sandbox Code Playgroud)

问题来自于授权过程之间的重定向,这个想法很有意思.如何将重定向更改为任何资源以避免此冲突?

编辑:

我想我更接近解决方案.我已经使用我的nodejs服务器测试,通过向所有请求添加Access-Control-Allow-Origin:*来支持cors而没有问题.

就像Stefan Isele已经提到的那样,Spring安全性似乎重定向或者没有添加CORS头部,这就是为什么请求似乎被破坏了.因此,虽然Spring安全性正在检查身份验证,但它必须添加正确的标头.

有谁知道怎么做?

编辑:

我找到了一个解决方法,这看起来很难看.我已经为spring boot启动了一个github问题,我在其中描述了解决方法:https://github.com/spring-projects/spring-boot/issues/5834

Séb*_*uze 38

Spring Security现在可以利用我写的这篇博客文章中描述的Spring MVC CORS支持.

要使其工作,您需要在Spring Security级别显式启用CORS支持,如下所示,否则在到达Spring MVC之前,Spring Security可能会阻止启用CORS的请求.

如果您正在使用控制器级@CrossOrigin注释,则只需启用Spring Security CORS支持,它将利用Spring MVC配置:

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.cors().and()...
    }
}
Run Code Online (Sandbox Code Playgroud)

如果您更喜欢使用CORS全局配置,则可以将CorsConfigurationSourcebean 声明如下:

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.cors().and()...
    }

    @Bean
    CorsConfigurationSource corsConfigurationSource() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", new CorsConfiguration().applyPermitDefaultValues());
        return source;
    }
}
Run Code Online (Sandbox Code Playgroud)

此方法取代了之前建议的基于过滤器的方法.

您可以在Spring Security文档的专用CORS部分中找到更多详细信息.

  • 我使用 `CorsFilter` bean 使 CORS+Spring Security 工作。由于某种原因,FilterRegistrationBean 没有被选中。 (2认同)

zho*_*uji 22

如果您使用JDK 8+,则有一行lambda解决方案:

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.cors().configurationSource(request -> new CorsConfiguration().applyPermitDefaultValues());
}
Run Code Online (Sandbox Code Playgroud)


Je *_*ick 16

如果您使用的是Spring Security,则可以执行以下操作以确保首先处理CORS请求:

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            // by default uses a Bean by the name of corsConfigurationSource
            .cors().and()
            ...
    }

    @Bean
    CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();
        configuration.setAllowedOrigins(Arrays.asList("https://example.com"));
        configuration.setAllowedMethods(Arrays.asList("GET","POST"));
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return source;
    }
}
Run Code Online (Sandbox Code Playgroud)

有关更多信息,请参见Spring 4.2.x CORS.


Sor*_*ami 14

如何解决 Spring Boot 2.3+ 上的 CORS

概括

如果您遇到此 CORS 问题,请不要担心。这是每个后端开发人员第一次尝试与前端微服务集成时都会遇到的常见问题。这是浏览器严格应用的某种安全策略,以确保用户的安全,这就是为什么当您通过 Postman/Swagger 或 cURL 尝试您的 API 时您没有面对它。

解决方案

  • 客户端绕过(仅限开发人员)

以下解决方案仅用于开发目的,您绝对需要为您的生产环境永久解决此 CORS 问题。您可以使用以下浏览器扩展来绕过针对 CORS 错误的浏览器策略,但如果它们无法正常工作,请不要感到惊讶。

  1. CORS 取消阻止Firefox - Chrome
  2. CORS 无处不在Firefox
  • 生产解决方案

在应用程序上配置 CORS 策略有多种方法,它完全基于您的部署架构。例如,如果您的应用程序将通过反向代理(如 Nginx)、API 网关(Kong)、服务网格 Sidecar 代理(即 Envoy)、Kubernetes NGINX Ingress 等公开,最佳实践是处理 CORS Edge 层上的配置,因为有时他们不考虑较低层的标头并覆盖它们,您仍然会收到来自浏览器的 CORS 错误。我在下面列出了用于配置边缘层的有用链接

但是,如果您要通过 SprintBoot 的内置 Web 服务器部署和公开您的 API,则可以使用接下来的说明。

全局启用 CORS 的说明 - Spring Boot 应用程序

如果您没有任何 WebSecurityConfig 实现,只需轻松执行以下步骤:

  1. 将以下依赖项 [ spring-boot-starter-security ] 添加到您的 pom.xml
<dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-security</artifactId>
</dependency>
Run Code Online (Sandbox Code Playgroud)
  1. 在您的配置包中创建一个扩展 WebSecurityConfig 的新类(即“SecurityConfig”)
  2. 将以下代码放入创建的文件中:
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.web.cors.CorsConfiguration;

import java.util.List;

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter
{


    @Override
    protected void configure(HttpSecurity http) throws Exception {
        CorsConfiguration corsConfiguration = new CorsConfiguration();
        corsConfiguration.setAllowedHeaders(List.of("Authorization", "Cache-Control", "Content-Type"));
        corsConfiguration.setAllowedOrigins(List.of("*"));
        corsConfiguration.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "PUT","OPTIONS","PATCH", "DELETE"));
        corsConfiguration.setAllowCredentials(true);
        corsConfiguration.setExposedHeaders(List.of("Authorization"));
        
        // You can customize the following part based on your project, it's only a sample
        http.authorizeRequests().antMatchers("/**").permitAll().anyRequest()
                .authenticated().and().csrf().disable().cors().configurationSource(request -> corsConfiguration);

    }
}
Run Code Online (Sandbox Code Playgroud)
  1. 现在您需要根据需要自定义 CORS 配置:

    • setAllowedHeaders -> 你必须指定哪些参数允许通过前端应用发送到后端服务,例如,如果你使用的是 Bearer/Basic Token Authorization 方法,则需要通过“授权”标题。因此,您需要确保 backed 会相应地接受此数据,为此,您必须将“授权”放在 Allowed-Headers 列表中。

    • setAllowedMethods -> 不要忘记将“OPTIONS”方法放在 Pre-flight 过程的列表中。别担心,在这里阅读更多!

    • setAllowCredentials -> 如果您使用 Authorization 标头,请将其设置为 True。

    • setExposedHeaders -> 如果你通过响应头返回数据,你需要在这里指定它们。例如,某些 API 旨在通过响应头在成功/身份验证后返回授权令牌。因此,需要相应地公开相关的标头。

    • setAllowedOrigins -> 您必须指定有资格向后端应用程序发送请求的域。例如,如果您的应用程序托管在https://penguin.com 上,而您的 API 位于https://api.penguin.com 上,则您需要允许“https://penguing.com”向您的后端发送请求. 此外,您可以传递通配符 (*) 以允许任何域向您的后端发送请求。但建议不要使用“any”,除非您提供公共 API 或在非生产环境中部署。

    对于可能认为 CORS 可以避免其他平台滥用 API(即网络钓鱼目的)的人来说,这是一个重要的误解。事实并非如此,CORS 策略是基于浏览器的策略,可以通过代理轻松绕过,因此它只会使误用过程更难一点,但不会产生免疫力。

  2. 构建/运行您的应用程序,测试您的 API,然后休息(每个人都知道 CORS 头痛)

替代解决方案

您可以使用以下链接:

Spring.io | 为 RESTful Web 服务启用跨域请求

贝尔东 | 带弹簧的 CORS

  • 这节省了我几个小时,谢谢!一个完全对我有用的小更改,将此行 corsConfiguration.setAllowedOrigins 更改为 corsConfiguration.setAllowedOriginPatterns (3认同)

imi*_*kay 12

// CorsConfig.java file
@Configuration
public class CorsConfig implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedOrigins("http://localhost:3000")
                .allowCredentials(true);
    }
}

Run Code Online (Sandbox Code Playgroud)

截至 2021 年,这可能是最简单的解决方案,只需要创建一个单独的类。

就这样。


Ste*_*com 8

跨源保护是浏览器的一项功能.正如你所假设的那样,Curl并不关心CORS.这就解释了为什么你的卷发成功,而浏览器请求不成功.

如果您使用错误的凭据发送浏览器请求,spring将尝试将客户端转发到登录页面.此响应(在登录页面之外)不包含标题"Access-Control-Allow-Origin",浏览器会按您的描述做出反应.

您必须使spring包含此登录响应的haeder,并且可能用于其他响应,例如错误页面等.

这可以这样做:

    @Configuration
    @EnableWebMvc
    public class WebConfig extends WebMvcConfigurerAdapter {

            @Override
            public void addCorsMappings(CorsRegistry registry) {
                registry.addMapping("/api/**")
                    .allowedOrigins("http://domain2.com")
                    .allowedMethods("PUT", "DELETE")
                    .allowedHeaders("header1", "header2", "header3")
                    .exposedHeaders("header1", "header2")
                    .allowCredentials(false).maxAge(3600);
            }
     }
Run Code Online (Sandbox Code Playgroud)

这是从cors-support-in-spring-framework中复制而来的

我首先要为所有资源添加cors映射:

registry.addMapping("/**")
Run Code Online (Sandbox Code Playgroud)

并允许所有方法标题..一旦它工作,你可能会开始再次减少到所需的最小值.

请注意,CORS配置随版本4.2而变化.

如果这不能解决您的问题,请发布您从失败的ajax请求中获得的响应.


小智 6

我通过以下方式解决了这个问题:

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;

    @Configuration
    public class CORSFilter extends CorsFilter {

        public CORSFilter(CorsConfigurationSource source) {
            super((CorsConfigurationSource) source);
        }

        @Override
        protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
                throws ServletException, IOException {

            response.addHeader("Access-Control-Allow-Headers",
                    "Access-Control-Allow-Origin, Origin, Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers");
            if (response.getHeader("Access-Control-Allow-Origin") == null)
                response.addHeader("Access-Control-Allow-Origin", "*");
            filterChain.doFilter(request, response);
        }

    }
Run Code Online (Sandbox Code Playgroud)

和:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

    @Configuration
    public class RestConfig {

        @Bean
        public CORSFilter corsFilter() {
            CorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
            CorsConfiguration config = new CorsConfiguration();
            config.addAllowedOrigin("http://localhost:4200");
            config.addAllowedMethod(HttpMethod.DELETE);
            config.addAllowedMethod(HttpMethod.GET);
            config.addAllowedMethod(HttpMethod.OPTIONS);
            config.addAllowedMethod(HttpMethod.PUT);
            config.addAllowedMethod(HttpMethod.POST);
            ((UrlBasedCorsConfigurationSource) source).registerCorsConfiguration("/**", config);
            return new CORSFilter(source);
        }
    }
Run Code Online (Sandbox Code Playgroud)


Kha*_*ifa 6

我在返回服务器状态的方法上遇到了同样的问题。该应用程序部署在多台服务器上。所以我发现最简单的方法是添加

@CrossOrigin(origins = "*")
@RequestMapping(value="/schedulerActive")
public String isSchedulerActive(){
  //code goes here
}
Run Code Online (Sandbox Code Playgroud)

此方法并不安全,但您可以添加allowCredentials该方法。


小智 6

@Bean
    public WebMvcConfigurer corsConfigurer() {
        return new WebMvcConfigurer() {
            @Override
            public void addCorsMappings(CorsRegistry registry) {
                registry.addMapping("/**").allowedOrigins("*").allowedMethods("*");
            }
        };
    }
Run Code Online (Sandbox Code Playgroud)

  • 不要只是添加答案,尝试解释你的答案。 (3认同)

Hub*_*iak 5

为 Spring-Boot、Spring-Security 和基于 Java 的配置找到了一个简单的解决方案:

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {
        httpSecurity.cors().configurationSource(new CorsConfigurationSource() {
            @Override
            public CorsConfiguration getCorsConfiguration(HttpServletRequest request) {
                return new CorsConfiguration().applyPermitDefaultValues();
            }
        });
    }
}
Run Code Online (Sandbox Code Playgroud)


小智 5

我通过以下方式解决了这个问题:`

@Bean
CorsConfigurationSource corsConfigurationSource() {
    CorsConfiguration configuration = new CorsConfiguration();
    configuration.setAllowedOrigins(Arrays.asList("*"));
    configuration.setAllowCredentials(true);
    configuration.setAllowedHeaders(Arrays.asList("Access-Control-Allow-Headers","Access-Control-Allow-Origin","Access-Control-Request-Method", "Access-Control-Request-Headers","Origin","Cache-Control", "Content-Type", "Authorization"));
    configuration.setAllowedMethods(Arrays.asList("DELETE", "GET", "POST", "PATCH", "PUT"));
    UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    source.registerCorsConfiguration("/**", configuration);
    return source;
}
Run Code Online (Sandbox Code Playgroud)

`