如何在spring boot+spring安全应用程序中配置cors?

kr98yfug  于 2021-07-13  发布在  Java
关注(0)|答案(13)|浏览(382)

我使用带有Spring Security 和cors支持的spring引导。
如果我执行以下代码

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

我得到了结果

200

如果我用错误的证书测试,比如

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

我没有得到401(这是springsecurity中错误身份验证的标准代码),而是得到了

0

具有以下浏览器通知:
得到http://localhost:5000/api/令牌
无法加载xmlhttprequesthttp://localhost:5000. 请求的资源上不存在“access control allow origin”标头。'原点'http://localhost:3000'因此不允许访问。响应具有http状态代码401。
我正在开发前端代码,需要从服务器响应中获得有用的http状态代码来处理这种情况。我需要比0更有用的东西。响应主体也是空的。我不知道我的配置是不是错了,或者是一个软件错误,我也不知道在哪里,是chromium(使用archlinux)还是spring安全。
我的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();
    }
}

如果我用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"}

而且证件有误:

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

编辑:避免误解。我用1.3.3Spring Boot。博客写道:
cors支持将在即将发布的springboot1.3版本中提供,并且已经在1.3.0.build-snapshot版本中提供。
在spring引导应用程序中使用带有@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("/**");
            }
        };
    }
}

不过,这个问题来自授权过程之间的重定向的想法很有趣。如何将重定向更改为任何资源以避免此冲突?
编辑:
我想我离解决问题更近了。我已经用我的nodejs服务器进行了测试,该服务器通过向所有请求添加访问控制allow origin:*来支持cors,没有任何问题。
就像stefanisele已经提到的那样,Spring Security 似乎重定向或没有添加cors头,所以请求似乎被破坏了。因此,当Spring Security 检查身份验证时,它必须添加适当的头。
有人知道怎么做吗?
编辑:
我找到了一个解决办法,看起来很难看。我已经开始为spring boot发布github,在这里我描述了解决方法:https://github.com/spring-projects/spring-boot/issues/5834

qcbq4gxm

qcbq4gxm1#

springsecurity现在可以利用springmvccors支持,这在我写的这篇博文中有描述。
要使其工作,您需要在spring安全级别显式启用cors支持,如下所示,否则在到达spring mvc之前,启用cors的请求可能会被Spring Security 阻止。
如果您使用的是控制器级别 @CrossOrigin 注解,您只需启用spring security cors支持,它将利用spring mvc配置:

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

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

如果您喜欢使用cors全局配置,可以声明 CorsConfigurationSource 如下所示:

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

这种方法取代了以前推荐的基于过滤器的方法。
您可以在spring安全文档的cors部分找到更多细节。

dfddblmv

dfddblmv2#

如果使用jdk 8+,则有一个单行lambda解决方案:

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.cors().configurationSource(request -> new CorsConfiguration().applyPermitDefaultValues());
}
szqfcxe2

szqfcxe23#

如果您使用的是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;
    }
}

有关更多信息,请参见Spring4.2.xCORS。
如果没有spring security,这将起作用:

@Bean
public WebMvcConfigurer corsConfigurer() {
    return new WebMvcConfigurer() {
        @Override
        public void addCorsMappings(CorsRegistry registry) {
            registry.addMapping("/**")
                    .allowedOrigins("*")
                    .allowedMethods("GET", "PUT", "POST", "PATCH", "DELETE", "OPTIONS");
        }
    };
}
v6ylcynt

v6ylcynt4#

跨源保护是浏览器的一项功能。curl不喜欢cors,就像你想象的那样。这就解释了为什么你的卷发是成功的,而浏览器的请求不是。
如果使用错误的凭据发送浏览器请求,spring将尝试将客户端转发到登录页。此响应(不在登录页上)不包含标题“访问控制允许源站”,浏览器的响应如您所述。
您必须使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);
            }
     }

这是从spring框架中的cors支持复制的
首先,我将为所有资源添加corsMap:

registry.addMapping("/**")

还允许所有方法的头。。一旦它起作用,你可以开始再次减少到所需的最低限度。
请注意,cors配置随4.2版而变化。
如果这不能解决您的问题,那么发布失败的ajax请求的响应。

t5zmwmid

t5zmwmid5#

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

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

    }

以及:

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);
        }
    }
insrf1ej

insrf1ej6#

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

suzh9iv87#

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

wz1wpwve8#

如何解决springboot2.3上的cors问题+

摘要

如果你面对的是cors问题,别担心。当每个后端开发人员第一次尝试与前端微服务集成时,这是一个常见的问题。这是某种安全策略,浏览器严格地应用于用户的安全,这就是为什么当你通过postman/swagger或curl尝试api时,你不会面对它。

解决方案

客户端旁路(仅适用于开发人员)

以下解决方案仅用于开发目的,您绝对需要为您的生产环境永久性地解决这个cors问题。您可以使用以下浏览器扩展绕过cors错误的浏览器策略,但如果这些策略不能正常工作,请不要感到惊讶。
cors取消阻止firefox-chrome
无处不在的cors firefox

生产解决方案

在应用程序上配置cors策略有不同的方法,完全基于您的部署体系结构。例如,如果您的应用程序将通过反向代理(如nginx)、api网关(kong)、service mesh sidecar代理(即特使)、kubernetes nginx入口等公开,最好的做法是处理边缘层上的cors配置,因为有时它们不考虑较低层的头,它们会覆盖它们,并且您仍然会从浏览器接收cors错误。我在下面列出了边缘层配置的有用链接
kubernetes nginx入口控制器cors-stackoverflow/官方用户手册
kong api网关-konghq
nginx-geekflare公司
Apache-geekflare
ha代理-官方网站
istio-讨论.istio
但是,如果您打算通过sprintboot的内置web服务器部署和公开api,那么您可以使用下面的说明。

全局启用cors的说明—spring引导应用程序

如果没有WebSecurity配置的任何实现,只需简单地执行以下步骤:
将以下依赖项[spring boot starter security]添加到pom.xml中

<dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-security</artifactId>
</dependency>

在您的配置包中创建一个新类,扩展websecurityconfig(即“securityconfig”)
将以下代码放入创建的文件中:

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

    }
}

现在您需要根据需要自定义cors配置:
setallowedheaders->您必须指定哪些参数允许通过前端应用程序发送到后端服务,例如,如果您使用的是承载/基本令牌授权方法,则需要通过“authorization”头传递jwt令牌。因此,您需要确保backed会相应地接受这些数据,为此,您必须在允许的头列表中添加“authorization”。
setallowedmethods->不要忘记将“选项”方法放在飞行前处理的列表中。别担心,在这里读更多!
setallowcredentials->如果使用的是授权标头,请将其设置为true。
setexposedheaders->如果通过响应头返回数据,则需要在此处指定它们。例如,一些api被设计成在成功/认证后通过响应头返回授权令牌。因此,需要相应地公开相关的报头。
setallowedorigins->必须指定有资格向后端应用程序发送请求的域。例如,如果您的应用程序托管在https://penguin.com and 你的API打开了https://api.penguin.com,您需要允许“https://penguing.com“向后端发送请求。此外,您还可以传递通配符(*),以允许任何域向后端发送请求。但建议不要使用“any”,除非您正在提供公共api或在非生产环境中部署。
有一个重要的误解,人们可能认为cors可以避免误用API的/在其他平台上(即钓鱼目的)。这不是真的,cors策略是基于浏览器的策略,可以通过代理轻松绕过,因此它只会使误用过程变得更加困难,但不会产生免疫力。
构建/运行应用程序、测试api和rest(每个人都知道cors)

替代解决方案

您可以使用以下链接:
spring.io |为restful web服务启用跨源请求
带Spring的bealdung | cors

qmb5sa22

qmb5sa229#

属性配置


# ENDPOINTS CORS CONFIGURATION (EndpointCorsProperties)

endpoints.cors.allow-credentials= # Set whether credentials are supported. When not set, credentials are not supported.
endpoints.cors.allowed-headers= # Comma-separated list of headers to allow in a request. '*' allows all headers.
endpoints.cors.allowed-methods=GET # Comma-separated list of methods to allow. '*' allows all methods.
endpoints.cors.allowed-origins= # Comma-separated list of origins to allow. '*' allows all origins. When not set, CORS support is disabled.
endpoints.cors.exposed-headers= # Comma-separated list of headers to include in a response.
endpoints.cors.max-age=1800 # How long, in seconds, the response from a pre-flight request can be cached by clients.
irlmq6kh

irlmq6kh10#

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

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

`

hs1ihplo

hs1ihplo11#

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

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

此方法不安全,但您可以添加 allowCredentials 为了这个。

xriantvc

xriantvc12#

cors可能是一个痛苦的屁股,但与这个简单的代码,你只是cors!!!!按指定方法

@CrossOrigin(origins="*")// in this line add your url and thats is all for spring boot side
    @GetMapping("/some")
    public String index() {
        return "pawned cors!!!!";
    }

就像春靴2.0.2中的魅力

92dk7w1h

92dk7w1h13#

我在axios、spring boot和spring身份验证安全性方面遇到了重大问题。
请注意spring boot的版本

相关问题