【问题标题】:Spring Boot Security CORSSpring Boot 安全 CORS
【发布时间】:2017-03-10 06:03:28
【问题描述】:

我在 spring 安全 URL 上的 CORS 过滤器有问题。 它不会在属于 spring sec(登录/注销)或被 Spring Security 过滤的 URL 上设置 Access-Control-Allow-Origin 和其他暴露的标头。

这是配置。

CORS:

@Configuration
@EnableWebMvc
public class MyWebMvcConfig extends WebMvcConfigurerAdapter {
********some irrelevant configs************
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/*").allowedOrigins("*").allowedMethods("GET", "POST", "OPTIONS", "PUT")
                .allowedHeaders("Content-Type", "X-Requested-With", "accept", "Origin", "Access-Control-Request-Method",
                        "Access-Control-Request-Headers")
                .exposedHeaders("Access-Control-Allow-Origin", "Access-Control-Allow-Credentials")
                .allowCredentials(true).maxAge(3600);
    }
}

安全性:

@Configuration
@EnableWebSecurity
public class OAuth2SecurityConfiguration extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .exceptionHandling().authenticationEntryPoint(authenticationEntryPoint).and()
                .formLogin()
                    .successHandler(ajaxSuccessHandler)
                    .failureHandler(ajaxFailureHandler)
                    .loginProcessingUrl("/authentication")
                    .passwordParameter("password")
                    .usernameParameter("username")
                .and()
                .logout()
                    .deleteCookies("JSESSIONID")
                    .invalidateHttpSession(true)
                    .logoutUrl("/logout")
                    .logoutSuccessUrl("/")
                .and()
                .csrf().disable()
                .anonymous().disable()
                .authorizeRequests()
                .antMatchers("/authentication").permitAll()
                .antMatchers("/oauth/token").permitAll()
                .antMatchers("/admin/*").access("hasRole('ROLE_ADMIN')")
                .antMatchers("/user/*").access("hasRole('ROLE_USER')");
    }
}

因此,如果我向安全性未侦听的 url 发出请求 - CORS 标头已设置。 Spring 安全 URL - 未设置。

Spring Boot 1.4.1

【问题讨论】:

    标签: spring spring-boot spring-mvc spring-security cors


    【解决方案1】:

    我尝试了以下配置并且成功了!

    @EnableWebSecurity
    public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable().cors().configurationSource(configurationSource()).and()
            .requiresChannel()
            .anyRequest()
            .requiresSecure();   
    }
    
    
    private CorsConfigurationSource configurationSource() {
          UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
          CorsConfiguration config = new CorsConfiguration();
          config.addAllowedOrigin("*");
          config.setAllowCredentials(true);
          config.addAllowedHeader("X-Requested-With");
          config.addAllowedHeader("Content-Type");
          config.addAllowedMethod(HttpMethod.POST);
          source.registerCorsConfiguration("/**", config);
          return source;
        }
     }
    

    【讨论】:

      【解决方案2】:

      这很干净,不需要任何额外的配置。在您希望所有选项有效的地方传递星号(就像我在 setAllowedHeaders 中所做的那样)。

      @EnableWebSecurity
      @Configuration
      @EnableGlobalMethodSecurity(prePostEnabled = true)
      public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
      
      @Override
        protected void configure(HttpSecurity httpSecurity) throws Exception {
          httpSecurity.cors().configurationSource(request -> {
            var cors = new CorsConfiguration();
            cors.setAllowedOrigins(List.of("http://localhost:4200", "http://127.0.0.1:80", "http://example.com"));
            cors.setAllowedMethods(List.of("GET","POST", "PUT", "DELETE", "OPTIONS"));
            cors.setAllowedHeaders(List.of("*"));
            return cors;
          }).and()...
        }
      }
      

      【讨论】:

      • 这应该是截至 2020 年 12 月的公认答案(使用 Spring Boot 2.3/2.4)
      • 有没有办法在没有 lambda 的情况下做到这一点并将其作为 Bean 放置?因为这个和@Dach0 解决方案对我有用,但这看起来更漂亮
      • 编辑:使用 CorsConfigurationSource 创建 Bean 有效,但需要 cors.setAllowedHeaders
      • 非常感谢。终于解决了我的问题。卡住了大约 3 周,但没有其他解决方案奏效!
      • 谢谢!但是我把 .cors().configurationSource(...) 放在 httpSecurity var 的末尾而不是你。但是你帮了很多忙!
      【解决方案3】:

      如果有人在 2020 年遇到同样的问题。这就是我的工作。这个应用程序是为了学习目的,所以我已经启用了一切

      CorsFilter 类:

      public class CorsFilter implements Filter {
      
          @Override
          public void init(FilterConfig filterConfig) throws ServletException {
      
          }
      
          @Override
          public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
              HttpServletResponse response = (HttpServletResponse) res;
              response.setHeader("Access-Control-Allow-Origin", "*");
              response.setHeader("Access-Control-Allow-Methods", "POST, PUT, GET, OPTIONS, DELETE");
              response.setHeader("Access-Control-Max-Age", "3600");
              response.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization, Content-Length, X-Requested-With");
              chain.doFilter(req, res);
          }
      
          @Override
          public void destroy() {
      
          }
      }
      

      然后在扩展 WebSecurityConfigurerAdapter 的类中再次设置标头:

      @Configuration
      @EnableWebSecurity
      public class SpringSecurityConfigurationBasicAuth extends WebSecurityConfigurerAdapter {
      
      @Bean
      CorsFilter corsFilter() {
          CorsFilter filter = new CorsFilter();
          return filter;
      }
      
          protected void configure(HttpSecurity http) throws Exception {
              System.out.println("Im configuring it");
              (
                      (HttpSecurity)
                              (
                                      (HttpSecurity)
                                              (
                                                      (ExpressionUrlAuthorizationConfigurer.AuthorizedUrl)
                                                              http
                                                                      .headers().addHeaderWriter(
                                                                      new StaticHeadersWriter("Access-Control-Allow-Origin", "*")).and()
                                                                      .addFilterBefore(corsFilter(), SessionManagementFilter.class)
                                                                      .csrf().disable()
                                                                      .authorizeRequests()
                                                                      .antMatchers(HttpMethod.OPTIONS,"/**").permitAll()
                                                                      .anyRequest()
      
                                              ).authenticated().and()
                              ).formLogin().and()
              ).httpBasic();
          }
      
      }
      

      【讨论】:

        【解决方案4】:

        我刚刚遇到了一个类似的问题,我试图从我在 React 中在 http://localhost:3000 上执行的前端向我在 SpringBoot 中在 http://localhost:8080 上执行的后端执行一个请求。我有两个错误:

        访问控制允许来源

        我通过将它添加到我的 RestController 中很容易地解决了这个问题:

        @CrossOrigin(origins = ["http://localhost:3000"])
        

        修复此问题后,我开始收到此错误: 响应中“Access-Control-Allow-Credentials”标头的值为“”,必须为“真”

        访问控制允许凭据

        这可以通过两种方式解决:

        1. allowCredentials = "true" 添加到 CrossOrigin 配置中:

          @CrossOrigin(origins = ["http://localhost:3000"], allowCredentials = "true")

        2. 在前端请求中更改获取的凭据选项。基本上,您需要像这样执行 fetch 调用:

          fetch('http://localhost:8080/your/api', { credentials: 'same-origin' })

        希望这会有所帮助 =)

        【讨论】:

          【解决方案5】:

          如果您需要它来快速进行本地开发,只需在您的控制器上添加此注释即可。 (根据需要更改原点)

          @CrossOrigin(origins = "http://localhost:4200", maxAge = 3600)
          

          【讨论】:

            【解决方案6】:

            您也可以使用拦截器来实现此目的。

            使用异常来确保您正在结束请求的生命周期:

            @ResponseStatus (
                value = HttpStatus.NO_CONTENT
            )
            public class CorsException extends RuntimeException
            {
            }
            

            然后,在您的拦截器中,为所有 OPTIONS 请求设置标头并抛出异常:

            public class CorsMiddleware extends HandlerInterceptorAdapter
            {
                @Override
                public boolean preHandle (
                    HttpServletRequest request,
                    HttpServletResponse response,
                    Object handler
                ) throws Exception
                {
                    if (request.getMethod().equals("OPTIONS")) {
                        response.addHeader("Access-Control-Allow-Origin", "*");
                        response.addHeader("Access-Control-Allow-Credentials", "true");
                        response.addHeader("Access-Control-Allow-Methods","GET, POST, PUT, OPTIONS, DELETE");
                        response.addHeader("Access-Control-Allow-Headers", "DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,Authorization,If-Modified-Since,Cache-Control,Content-Type");
                        response.addHeader("Access-Control-Max-Age", "3600");
                        response.addHeader("charset", "utf-8");
                        throw new CorsException();
                    }
            
                    return super.preHandle(request, response, handler);
                }
            }
            

            最后,将拦截器应用于所有路由:

            @Configuration
            public class MiddlewareConfig extends WebMvcConfigurerAdapter
            {
                @Override
                public void addInterceptors (InterceptorRegistry registry)
                {
                    registry.addInterceptor(new CorsMiddleware())
                            .addPathPatterns("/**");
                }
            }
            

            【讨论】:

              【解决方案7】:

              目前,如果启用安全性,则默认情况下会阻止 OPTIONS 请求。

              只需添加一个额外的 bean,预检请求就会得到正确处理:

                 @Bean
                 public IgnoredRequestCustomizer optionsIgnoredRequestsCustomizer() {
                    return configurer -> {
                       List<RequestMatcher> matchers = new ArrayList<>();
                       matchers.add(new AntPathRequestMatcher("/**", "OPTIONS"));
                       configurer.requestMatchers(new OrRequestMatcher(matchers));
                    };
                 }
              

              请注意,根据您的应用程序,这可能会为潜在的攻击打开它。

              打开问题以获得更好的解决方案:https://github.com/spring-projects/spring-security/issues/4448

              【讨论】:

                【解决方案8】:

                我有一个基于 React 的 Web 客户端,我的后端 REST API 正在运行 Spring Boot Ver 1.5.2

                我想在localhost:8080 上运行的客户端的所有控制器路由请求上快速启用CORS。在我的安全配置中,我只是添加了 FilterRegistrationBean 类型的 @Bean 并让它轻松工作。

                代码如下:

                @Configuration
                @EnableWebSecurity
                @EnableGlobalMethodSecurity(prePostEnabled = true)
                public class AuthConfiguration extends WebSecurityConfigurerAdapter {
                
                ....
                ....
                
                  @Bean
                  public FilterRegistrationBean corsFilter() {
                    final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
                    CorsConfiguration config = new CorsConfiguration();
                    config.setAllowCredentials(true);
                    config.addAllowedOrigin(corsAllowedOrigin); // @Value: http://localhost:8080
                    config.addAllowedHeader("*");
                    config.addAllowedMethod("*");
                    source.registerCorsConfiguration("/**", config);
                    FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));
                    bean.setOrder(0);
                    return bean;
                  }
                
                  @Override
                  protected void configure(HttpSecurity httpSecurity) throws Exception {    
                      httpSecurity
                        .authorizeRequests()
                        .antMatchers(HttpMethod.OPTIONS, "/**").permitAll() // **permit OPTIONS call to all**
                        ....
                  }
                
                ....
                ....
                
                }
                

                可以参考Spring Boot docs here

                【讨论】:

                  【解决方案9】:

                  选项 1(使用 WebMvcConfigurer bean):

                  您开始使用的 CORS 配置不是使用 Spring Boot 执行此操作的正确方法。你需要注册一个WebMvcConfigurer bean。参考here

                  Spring Boot CORS 配置示例:

                  @Configuration
                  @Profile("dev")
                  public class DevConfig {
                  
                      @Bean
                      public WebMvcConfigurer corsConfigurer() {
                          return new WebMvcConfigurerAdapter() {
                              @Override
                              public void addCorsMappings(CorsRegistry registry) {
                                  registry.addMapping("/**").allowedOrigins("http://localhost:4200");
                              }
                          };
                      }
                  
                  }
                  

                  这将为基本(无安全启动器)Spring Boot 应用程序提供 CORS 配置。请注意,CORS 支持独立于 Spring Security。

                  一旦你引入了 Spring Security,你需要在你的安全配置中注册 CORS。 Spring Security 足够聪明,可以获取您现有的 CORS 配置。

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

                  选项 2(使用 CorsConfigurationSource bean):

                  我描述的第一个选项实际上是从将 Spring Security 添加到现有应用程序的角度来看的。如果您从一开始就添加 Spring Security,Spring Security Docs 中概述的方式涉及添加 CorsConfigurationSource bean。

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

                  【讨论】:

                  • 好像 bean 名称必须是 'corsConfigurationSource' 还是没有被拾取?
                  • 我在 cors() 处出错。 'HttpSecurity 类型的方法 cors() 未定义'
                  • @PawanTiwari 在此方法签名中使用 protected void configure(HttpSecurity http)
                  • @ThomasRS 是的,方法必须是corsConfigurationSource() 或者你需要用@Bean("corsConfigurationSource") 命名你的bean。查找此 bean 的代码是 org.springframework.security.config.annotation.web.configurers.CorsConfigurer
                  • "您需要在您的安全配置中注册 CORS。"这句话拯救了我的一天。
                  【解决方案10】:

                  您可以编写自己的 CorsFilter 并将其添加到您的安全配置中,而不是使用 CorsRegistry。

                  自定义 CorsFilter 类:

                  public class CorsFilter implements Filter {
                  
                      @Override
                      public void init(FilterConfig filterConfig) throws ServletException {
                  
                      }
                  
                      @Override
                      public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
                          HttpServletResponse response = (HttpServletResponse) servletResponse;
                          HttpServletRequest request= (HttpServletRequest) servletRequest;
                  
                          response.setHeader("Access-Control-Allow-Origin", "*");
                          response.setHeader("Access-Control-Allow-Methods", "GET,POST,DELETE,PUT,OPTIONS");
                          response.setHeader("Access-Control-Allow-Headers", "*");
                          response.setHeader("Access-Control-Allow-Credentials", true);
                          response.setHeader("Access-Control-Max-Age", 180);
                          filterChain.doFilter(servletRequest, servletResponse);
                      }
                  
                      @Override
                      public void destroy() {
                  
                      }
                  }
                  

                  安全配置类:

                  @Configuration
                  @EnableWebSecurity
                  public class OAuth2SecurityConfiguration extends WebSecurityConfigurerAdapter {
                  
                      @Bean
                      CorsFilter corsFilter() {
                          CorsFilter filter = new CorsFilter();
                          return filter;
                      }
                  
                      @Override
                      protected void configure(HttpSecurity http) throws Exception {
                          http
                                  .addFilterBefore(corsFilter(), SessionManagementFilter.class) //adds your custom CorsFilter
                                  .exceptionHandling().authenticationEntryPoint(authenticationEntryPoint).and()
                                  .formLogin()
                                      .successHandler(ajaxSuccessHandler)
                                      .failureHandler(ajaxFailureHandler)
                                      .loginProcessingUrl("/authentication")
                                      .passwordParameter("password")
                                      .usernameParameter("username")
                                  .and()
                                  .logout()
                                      .deleteCookies("JSESSIONID")
                                      .invalidateHttpSession(true)
                                      .logoutUrl("/logout")
                                      .logoutSuccessUrl("/")
                                  .and()
                                  .csrf().disable()
                                  .anonymous().disable()
                                  .authorizeRequests()
                                  .antMatchers("/authentication").permitAll()
                                  .antMatchers("/oauth/token").permitAll()
                                  .antMatchers("/admin/*").access("hasRole('ROLE_ADMIN')")
                                  .antMatchers("/user/*").access("hasRole('ROLE_USER')");
                      }
                  }
                  

                  【讨论】:

                  • 似乎这是关键.addFilterBefore(corsFilter(), SessionManagementFilter.class) 谢谢。没有教程指向它。
                  • 这是迄今为止处理 CORS 和 Sprint 启动安全性的最佳答案。
                  • 虽然这可行,但应该按照@The Gilbert Arenas Dagger 在他的选项 1 中建议的方式将 spring mvc 与 spring security 结合起来。
                  • 这不起作用,因为当凭证模式为“包含”时,您不能使用通配符“*”。我找到的解决方案是在设置“Access-Control-Allow-Origin”标头时将“*”替换为 request.getHeader(“Origin”)。这对我有用。
                  • 对于任何像我一样碰巧和迷路的人,我实现的Filterjavax.servlet.Filter。像魅力一样工作。可能看起来很明显,但希望这可以节省我花费在 Google 搜索上的时间。
                  猜你喜欢
                  • 2016-07-14
                  • 2020-05-29
                  • 2020-02-02
                  • 2019-03-02
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多