如何在 Spring Boot + Spring Security 应用程序中配置 CORS? [英] How to configure CORS in a Spring Boot + Spring Security application?

查看:62
本文介绍了如何在 Spring Boot + Spring Security 应用程序中配置 CORS?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用带有 Spring Security 和 Cors 支持的 Spring Boot.

如果我执行以下代码

url = 'http://localhost:5000/api/token'xmlhttp = 新的 XMLHttpRequestxmlhttp.onreadystatechange = ->如果 xmlhttp.readyState 是 4console.log xmlhttp.statusxmlhttp.open "GET", url, true# xmlhttp.setRequestHeader "X-Requested-With", "XMLHttpRequest"xmlhttp.setRequestHeader '授权', '基本' + btoa 'a:a'做 xmlhttp.send

我得到了结果

200

如果我使用错误的凭据进行测试,例如

url = 'http://localhost:5000/api/token'xmlhttp = 新的 XMLHttpRequestxmlhttp.onreadystatechange = ->如果 xmlhttp.readyState 是 4console.log xmlhttp.statusxmlhttp.open "GET", url, true# xmlhttp.setRequestHeader "X-Requested-With", "XMLHttpRequest"xmlhttp.setRequestHeader '授权', '基本' + btoa 'a:aa'做 xmlhttp.send

我得到的不是 401(这是 Spring Security 中错误身份验证的标准代码)

<预><代码>0

带有以下浏览器通知:

GET http://localhost:5000/api/token

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

我正在开发前端代码,需要来自服务器响应的有用 http 状态代码来处理这种情况.我需要比 0 更有用的东西.而且响应体是空的.我不知道我的配置是错误的,还是软件错误,我也不知道在哪里,是chromium(使用arch linux)还是spring security.

我的 Spring 配置是:

@SpringBootApplication公共类应用{公共静态无效主(字符串 [] args){SpringApplication.run(Application.class, args);}}@RestController@RequestMapping("api")公共类控制器{@RequestMapping("令牌")@CrossOrigin映射<字符串,字符串>令牌(HttpSession 会话){返回 Collections.singletonMap("token", session.getId());}}@启用网络安全公共类 WebSecurityConfiguration 扩展了 WebSecurityConfigurerAdapter {@覆盖受保护的无效配置(AuthenticationManagerBuilder auth)抛出异常{auth.inMemoryAuthentication().withUser("a").password("a").roles("USER");}@覆盖protected void configure(HttpSecurity http) 抛出异常 {http.authorizeRequests().requestMatchers(CorsUtils::isPreFlightRequest).permitAll().anyRequest().authenticated().and().httpBasic();}}

如果我用 curl 测试一切正常,我认为是因为不需要 CORS 支持,但我尝试使用 OPTION 请求模拟 CORS,结果也不错.

$ curl -v localhost:5000/api/token -H "Authorization: Basic YTpha"* 正在尝试 ::1 ...* 连接到本地主机 (::1) 端口 5000 (#0)>获取/api/token HTTP/1.1>主机:本地主机:5000>用户代理:curl/7.48.0>接受: */*>授权:基本YTpha><HTTP/1.1 200 正常<服务器:Apache-Coyote/1.1<X-Content-Type-Options: nosniff<X-XSS-保护:1;模式=块<缓存控制:无缓存,无存储,max-age=0,必须重新验证<编译指示:无缓存<过期时间:0<X-Frame-Options:拒绝<访问控制允许来源:http://localhost:3000<访问控制允许方法:POST、GET、OPTIONS、DELETE<访问控制最大年龄:3600<访问控制允许凭据: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<内容类型:application/json;charset=UTF-8<传输编码:分块<日期:2016 年 5 月 1 日星期日 16:15:44 GMT<* 到主机 localhost 的连接 #0 保持不变{令牌":58e4cca9-7719-46c8-9180-2fc16aec8dff"}

并且凭据错误:

$ curl -v localhost:5000/api/token -H "Authorization: Basic YTp"* 正在尝试 ::1 ...* 连接到本地主机 (::1) 端口 5000 (#0)>获取/api/token HTTP/1.1>主机:本地主机:5000>用户代理:curl/7.48.0>接受: */*>授权:基本 YTp><HTTP/1.1 401 未经授权<服务器:Apache-Coyote/1.1<X-Content-Type-Options: nosniff<X-XSS-保护:1;模式=块<缓存控制:无缓存,无存储,max-age=0,必须重新验证<编译指示:无缓存<过期时间:0<X-Frame-Options:拒绝<WWW-Authenticate:基本领域=领域"<内容类型:application/json;charset=UTF-8<传输编码:分块<日期:2016 年 5 月 1 日星期日 16:16:15 GMT<* 到主机 localhost 的连接 #0 保持不变{"timestamp":1462119375041,"status":401,"error":"Unauthorized","message":"无法解码基本身份验证令牌","path":"/api/token"}

以免误会.我使用 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公共类 MyConfiguration {@豆角,扁豆公共 WebMvcConfigurer corsConfigurer() {返回新的 WebMvcConfigurerAdapter() {@覆盖公共无效 addCorsMappings(CorsRegistry 注册表){registry.addMapping("/**");}};}}

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

我想我更接近解决方案了.我已经使用我的 nodejs 服务器进行了测试,该服务器通过添加支持 cors 没有问题访问控制允许来源:*对所有请求.

就像 Stefan Isele 已经提到的那样,似乎 spring 安全重定向或不添加 CORS 标头,因此这就是请求似乎被破坏的原因.因此,当 Spring Security 正在检查身份验证时,它必须添加正确的标头.

有人知道怎么做吗?

我找到了一个解决方法,这似乎很难看.我已经开始了 Spring Boot 的 github 问题,我描述了解决方法:https://github.com/spring-projects/spring-boot/issues/5834

解决方案

Spring Security 现在可以利用 这篇博文 我写的.

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

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

@EnableWebSecurity公共类 WebSecurityConfig 扩展了 WebSecurityConfigurerAdapter {@覆盖protected void configure(HttpSecurity http) 抛出异常 {http.cors().and()...}}

如果你更喜欢使用 CORS 全局配置,你可以声明一个 CorsConfigurationSource bean,如下所示:

@EnableWebSecurity公共类 WebSecurityConfig 扩展了 WebSecurityConfigurerAdapter {@覆盖protected void configure(HttpSecurity http) 抛出异常 {http.cors().and()...}@豆角,扁豆CorsConfigurationSource corsConfigurationSource() {UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();source.registerCorsConfiguration("/**", new CorsConfiguration().applyPermitDefaultValues());返回源;}}

这种方法取代了 之前推荐的基于过滤器的方法.

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

I use Spring Boot with Spring Security and Cors Support.

If I execute following code

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

I get as a result

200

If I test with wrong credentials like

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

instead of getting 401 (that is the standard code for wrong authentication in spring security) I get

0

with following browser notification:

GET http://localhost:5000/api/token

XMLHttpRequest cannot load http://localhost:5000. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:3000' is therefore not allowed access. The response had HTTP status code 401.

I'm developing front-end code that needs useful http status codes from server responses to handle the situation. I need something more useful than 0. Also the response body is empty. I dont know if my config is wrong, or it's a software bug and I also don't know where, if it's chromium (using arch linux) or spring security.

My Spring Config is:

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

If I test with curl everything works perfect, I think because no CORS support needed, but I tried to simulate the CORS with OPTION requests and the result was also ok.

$ 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"}

and with wrong credentials:

$ 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"}

Edit: To avoid misunderstandings. I use 1.3.3 Spring Boot. The Blog post writes:

CORS support will be available in the upcoming Spring Boot 1.3 release, and is already available in the 1.3.0.BUILD-SNAPSHOT builds.

Using controller method CORS configuration with @CrossOrigin annotations in your Spring Boot application does not require any specific configuration.

Global CORS configuration can be defined by registering a WebMvcConfigurer bean with a customized addCorsMappings(CorsRegistry) method:

I have added following code to enable global cors support. actually I have tried this before but it the result was the same. I tried it again recently and the result is the same.

@Configuration
public class MyConfiguration {

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

The idea, that the problem comes from a redirect between the authorization process is an interesting though. how can i change the redirect to any resources to avoid this conflict?

EDIT:

I guess I am closer to a solution. I have tested with my nodejs server that supports cors without problems by adding Access-Control-Allow-Origin: * to all requests.

Like Stefan Isele has already mentioned it seems that spring security redirects or doesn't add the CORS header so that's why the request seems to be broken. So while spring security is checking the authentification it has to add the proper header.

Does anyone know how to do so?

EDIT:

I found a workaround, that seems to be ugly. I have started a github issue for spring boot where I describe the workaround: https://github.com/spring-projects/spring-boot/issues/5834

解决方案

Spring Security can now leverage Spring MVC CORS support described in this blog post I wrote.

To make it work, you need to explicitly enable CORS support at Spring Security level as following, otherwise CORS enabled requests may be blocked by Spring Security before reaching Spring MVC.

If you are using controller level @CrossOrigin annotations, you just have to enable Spring Security CORS support and it will leverage Spring MVC configuration:

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

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

If you prefer using CORS global configuration, you can declare a CorsConfigurationSource bean as following:

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

This approach supersedes the filter-based approach previously recommended.

You can find more details in the dedicated CORS section of Spring Security documentation.

这篇关于如何在 Spring Boot + Spring Security 应用程序中配置 CORS?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆