首页 文章

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

提问于
浏览
32

我使用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

我得到了结果

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(这是 Spring 季安全中错误认证的标准代码)我得到了

0

以下浏览器通知:

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

如果我用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.3 Spring Boot . 博客文章写道:

即将推出的Spring Boot 1.3版本将提供CORS支持,并且已在1.3.0.BUILD-SNAPSHOT版本中提供 . 在Spring Boot应用程序中使用带有@CrossOrigin注释的控制器方法CORS配置不需要任何特定配置 . 可以通过使用自定义的addCorsMappings(CorsRegistry)方法注册WebMvcConfigurer bean来定义全局CORS配置:

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

@Configuration
public class MyConfiguration {

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

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

编辑:

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

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

有谁知道怎么做?

编辑:

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

10 回答

  • 1

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

    Spring Security现在可以利用_9925326中描述的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()...
        }
    }
    

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

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

    这种方法取代了以前推荐的filter-based approach .

    您可以在Spring Security文档的dedicated CORS section中找到更多详细信息 .

  • 1

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

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

  • 9

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

    @EnableWebSecurity
    public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.cors().configurationSource(request -> new CorsConfiguration().applyPermitDefaultValues());
    }
    
  • 24

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

    如果您使用错误的凭据发送浏览器请求,spring将尝试将客户端转发到登录页面 . 此响应(在登录页面之外)不包含 Headers “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);
                }
         }
    

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

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

    registry.addMapping("/**")
    

    并且还允许所有方法 Headers ..一旦它工作,你可以开始再次减少到所需的最小值 .

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

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

  • 12

    对于属性配置

    # 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.
    
  • 6

    我解决了这个问题:`

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

    `

  • 2

    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!!!!";
        }
    

    就像 spring 靴2.0.2的魅力

  • 2

    我解决了这个问题问题:

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

    和:

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

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

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

    此方法不安全,但您可以为此添加 allowCredentials .

相关问题