【问题标题】:CORS with spring-boot and angularjs not working带有 spring-boot 和 angularjs 的 CORS 不起作用
【发布时间】:2021-07-31 04:30:18
【问题描述】:

我正在尝试从另一个(angularjs)调用一个应用程序(spring-boot 应用程序)上的 REST 端点。应用程序正在以下主机和端口上运行。

  • REST 应用程序,使用 Spring Boot,http://localhost:8080
  • HTML 应用程序,使用 angularjs,http://localhost:50029

我还在 spring-boot 应用程序中使用 spring-security。从 HTML 应用程序中,我可以对 REST 应用程序进行身份验证,但此后,我仍然无法访问任何 REST 端点。例如,我有一个 angularjs 服务定义如下。

adminServices.factory('AdminService', ['$resource', '$http', 'conf', function($resource, $http, conf) {
    var s = {};
    s.isAdminLoggedIn = function(data) {
        return $http({
            method: 'GET',
            url: 'http://localhost:8080/api/admin/isloggedin',
            withCredentials: true,
            headers: {
                'X-Requested-With': 'XMLHttpRequest'
            }
        });
    };
    s.login = function(username, password) {
        var u = 'username=' + encodeURI(username);
        var p = 'password=' + encodeURI(password);
        var r = 'remember_me=1';
        var data = u + '&' + p + '&' + r;

        return $http({
            method: 'POST',
            url: 'http://localhost:8080/login',
            data: data,
            headers: {'Content-Type': 'application/x-www-form-urlencoded'}
        });
    };
    return s;
}]);

angularjs 控制器如下所示。

adminControllers.controller('LoginController', ['$scope', '$http', 'AdminService', function($scope, $http, AdminService) {
    $scope.username = '';
    $scope.password = '';

    $scope.signIn = function() {
        AdminService.login($scope.username, $scope.password)
            .success(function(d,s) {
                if(d['success']) {
                    console.log('ok authenticated, call another REST endpoint');
                    AdminService.isAdminLoggedIn()
                        .success(function(d,s) {
                            console.log('i can access a protected REST endpoint after logging in');
                        })
                        .error(function(d, s) { 
                            console.log('huh, error checking to see if admin is logged in');
                            $scope.reset();
                        });
                } else {
                    console.log('bad credentials?');
                }
            })
            .error(function(d, s) {
                console.log('huh, error happened!');
            });
    };
}]);

在拨打http://localhost:8080/api/admin/isloggedin 时,我收到了401 Unauthorized

在 REST 应用程序端,我有一个如下所示的 CORS 过滤器。

@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class CORSFilter implements Filter {

    @Override
    public void destroy() { }

    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
            throws IOException, ServletException {
        HttpServletResponse response = (HttpServletResponse) res;
        HttpServletRequest request = (HttpServletRequest) req;

        response.setHeader("Access-Control-Allow-Origin", "http://localhost:50029");
        response.setHeader("Access-Control-Allow-Methods", "POST, PUT, GET, OPTIONS, DELETE");
        response.setHeader("Access-Control-Max-Age", "3600");
        response.setHeader("Access-Control-Allow-Headers", "X-Requested-With, X-Auth-Token");
        response.setHeader("Access-Control-Allow-Credentials", "true");

        if(!"OPTIONS".equalsIgnoreCase(request.getMethod())) {
            chain.doFilter(req, res);
        }
    }

    @Override
    public void init(FilterConfig config) throws ServletException { }
}

我的 spring 安全配置如下所示。

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private RestAuthenticationEntryPoint restAuthenticationEntryPoint;

    @Autowired
    private JsonAuthSuccessHandler jsonAuthSuccessHandler;

    @Autowired
    private JsonAuthFailureHandler jsonAuthFailureHandler;

    @Autowired
    private JsonLogoutSuccessHandler jsonLogoutSuccessHandler;

    @Autowired
    private AuthenticationProvider authenticationProvider;

    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    private PersistentTokenRepository persistentTokenRepository;

    @Value("${rememberme.key}")
    private String rememberMeKey;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .csrf().disable()
            .exceptionHandling()
            .authenticationEntryPoint(restAuthenticationEntryPoint)
                .and()
            .authorizeRequests()
                .antMatchers("/api/admin/**").hasRole("ADMIN")
                .antMatchers("/", "/admin", "/css/**", "/js/**", "/fonts/**", "/api/**").permitAll()
                .anyRequest().authenticated()
                .and()
            .formLogin()
                .successHandler(jsonAuthSuccessHandler)
                .failureHandler(jsonAuthFailureHandler)
                .permitAll()
                .and()
            .logout()
                .deleteCookies("remember-me", "JSESSIONID")
                .logoutSuccessHandler(jsonLogoutSuccessHandler)
                .permitAll()
                .and()
            .rememberMe()
                .userDetailsService(userDetailsService)
                .tokenRepository(persistentTokenRepository)
                .rememberMeCookieName("REMEMBER_ME")
                .rememberMeParameter("remember_me")
                .tokenValiditySeconds(1209600)
                .useSecureCookie(false)
                .key(rememberMeKey);
    }

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth
            .authenticationProvider(authenticationProvider);
    }
}

所有处理程序所做的只是根据用户是否登录、身份验证失败或注销来写出类似{success: true} 的 JSON 响应。 RestAuthenticationEntryPoint 如下所示。

@Component
public class RestAuthenticationEntryPoint implements AuthenticationEntryPoint {

    @Override
    public void commence(HttpServletRequest req, HttpServletResponse resp, AuthenticationException ex)
            throws IOException, ServletException {
        resp.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
    }

}

对我遗漏或做错了什么有任何想法?

【问题讨论】:

  • 我想您还需要进行身份验证,例如令牌或其他东西。您有 2 台服务器。你看过那个教程吗?spring.io/guides/tutorials/spring-security-and-angular-js
  • @GokhanOner 我如何进行身份验证?这可能是这个问题的缺失部分。另外,是的,我确实阅读了这些教程,并且认为它们与我的方法不一致。前两部分处理 Http-Basic 身份验证,然后第三部分处理 Redis(我不想或不打算将其作为依赖项),然后最后一个教程是关于 API Gateway with spring cloud,我认为有点矫枉过正。
  • 我想你可以不使用redis,它只是一个键值缓存存储。您需要将身份验证和 CSRF 令牌存储在存储中,可能在动态内部映射。这里的关键是身份验证密钥。查看示例:github.com/dsyer/spring-security-angular/tree/master/… 和带有“资源服务器”的页面。您将看到定义了一些额外的 bean,CORS 过滤器的顺序也很重要。还有一些道具。也需要改变。
  • 好的,我做了一个快速的研究。要摆脱 Redis,您只需要创建一个 springSessionRepositoryFilter bean,查看github.com/spring-projects/spring-session/blob/1.0.0.RC1/…,还有 sessionRepository bean,在这个 bean 中,您可以使用 MapSessionRepository,而不是 RedisOperationsSessionRepository,它也在 spring-session 中。然后按照例子来。

标签: angularjs rest spring-mvc spring-boot cors


【解决方案1】:

要在全球范围内启用 CORS,您需要在两个地方进行更改:

1.春季启动:

@Configuration
public class CorsConfiguration extends WebMvcConfigurationSupport {  
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**").allowedOrigins("*").allowedMethods("*")
        .allowCredentials(true);
    }
}

你可以在WebMvcConfigurerAdapter中做同样的事情,或者创建WebMvcConfigurer的bean。

2。春季安全

@Override
    protected void configure(HttpSecurity http) throws Exception {
        http.cors().and()
                .authorizeRequests()
                .antMatchers(HttpMethod.OPTIONS).permitAll() //Permits your preflight request
}

在 Spring Boot 2.3.3.RELEASE 上工作

【讨论】:

    【解决方案2】:

    我很惊讶地发现 Eduardo Dennis 指的是最新的解决方案,它更简单且不需要编写自己的过滤器类:它使用

    • org.springframework.web.bind.annotation.CrossOrigin 控制器上的注释
    • 并将and().cors() 包含到您的 Spring Security 配置中。

    这就是你所要做的!

    你可以像这样使用@CrossOrigin注解:

    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.CrossOrigin;
    import org.springframework.web.bind.annotation.RequestMapping;
    
    @Controller
    @RequestMapping("/api")
    @CrossOrigin
    public class BackendController {
        ...
    }
    

    如果要配置allowedHeadersmethodsorigins 等,只需将这些值添加到注释中,如下所示:@CrossOrigin(origins = "http://localhost:50029", maxAge = 3600)

    使用@CrossOrigin 注解,Spring Security 配置变得极其简单。只需将and().cors() 添加到您的WebSecurityConfig.java 类:

    @Configuration
    @EnableWebSecurity
    public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http
                .csrf().disable()
                .and().cors()
                ...
        }
    

    就是这样!您可以删除您的 Filter/CORSFilter 类。如果你想添加一个全局配置,你也可以声明一个CorsConfigurationSource。请参阅this great answerthis blog post by Sébastien Deleuze)。 Spring开发人员也明确表示:

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

    因此,接受的答案已过时。这也是一个完整的示例项目:https://github.com/jonashackt/microservice-api-spring-boot

    【讨论】:

    • 这也是错误的。 .cors() 需要在 disable 之后,然后 .and()
    • 另外,即使顺序正确,这也不起作用。阻止我从 localhost:4200 到 localhost:8080 的所有请求
    • 好吧,也许您可​​以向我们展示示例代码? “错误”在这里非常苛刻——因为这个建议直接来自 Spring 开发团队。并且实现的 Builder Pattern 不应该关心任何顺序。最后但并非最不重要的一点:我链接到一个示例项目,该项目显示了一个工作解决方案,其中包含我在此处描述的代码,所以也许你应该看看你的代码,如果有不同的东西干扰 Spring 安全配置。
    【解决方案3】:

    这对我来说是为了在 Spring boot 和 React 之间禁用 CORS

    @Configuration
    public class CorsConfig implements WebMvcConfigurer {
    
        /**
         * Overriding the CORS configuration to exposed required header for ussd to work
         *
         * @param registry CorsRegistry
         */
    
        @Override
        public void addCorsMappings(CorsRegistry registry) {
            registry.addMapping("/**")
                    .allowedOrigins("*")
                    .allowedMethods("*")
                    .allowedHeaders("*")
                    .allowCredentials(true)
                    .maxAge(4800);
        }
    }
    

    我还必须修改安全配置,如下所示:

            @Override
            protected void configure(HttpSecurity http) throws Exception {
                http.csrf().disable()
                        .cors().configurationSource(new CorsConfigurationSource() {
    
                    @Override
                    public CorsConfiguration getCorsConfiguration(HttpServletRequest request) {
                        CorsConfiguration config = new CorsConfiguration();
                        config.setAllowedHeaders(Collections.singletonList("*"));
                        config.setAllowedMethods(Collections.singletonList("*"));
                        config.addAllowedOrigin("*");
                        config.setAllowCredentials(true);
                        return config;
                    }
                }).and()
                        .antMatcher("/api/**")
                        .authorizeRequests()
                        .anyRequest().authenticated()
                        .and().httpBasic()
                        .and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                        .and().exceptionHandling().accessDeniedHandler(apiAccessDeniedHandler());
            }
    

    【讨论】:

      【解决方案4】:

      只需创建一个单个类,这样一切都会好起来的:

              @Component
              @Order(Ordered.HIGHEST_PRECEDENCE)
              public class MyCorsConfig implements Filter {
      
                  @Override
                  public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
                      final 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-Allow-Headers", "Authorization, Content-Type, enctype");
                      response.setHeader("Access-Control-Max-Age", "3600");
                      if (HttpMethod.OPTIONS.name().equalsIgnoreCase(((HttpServletRequest) req).getMethod())) {
                          response.setStatus(HttpServletResponse.SC_OK);
                      } else {
                          chain.doFilter(req, res);
                      }
                  }
      
                  @Override
                  public void destroy() {
                  }
      
                  @Override
                  public void init(FilterConfig config) throws ServletException {
                  }
              }
      

      【讨论】:

        【解决方案5】:

        在我们的 Spring Boot 应用程序中,我们已经像这样设置了 CorsConfigurationSource。

        首先添加allowedOrigns 然后设置applyPermitDefaultValues() 的顺序让Spring 为允许的标头、公开的标头、允许的方法等设置默认值,因此我们不必指定这些。

            public CorsConfigurationSource corsConfigurationSource() {
                CorsConfiguration configuration = new CorsConfiguration();
                configuration.setAllowedOrigins(Arrays.asList("http://localhost:8084"));
                configuration.applyPermitDefaultValues();
        
                UrlBasedCorsConfigurationSource configurationSource = new UrlBasedCorsConfigurationSource();
                configurationSource.registerCorsConfiguration("/**", configuration);
                return configurationSource;
            }
        
            @Override
            protected void configure(HttpSecurity http) throws Exception {
        
                http.authorizeRequests()
                        .antMatchers("/api/**")
                        .access("@authProvider.validateApiKey(request)")
                        .anyRequest().authenticated()
                        .and().cors()
                        .and().csrf().disable()
                        .httpBasic().authenticationEntryPoint(authenticationEntryPoint);
        
                http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
            }
        

        【讨论】:

          【解决方案6】:

          第 1 步

          通过使用@CrossOrigin 注释来注释控制器将允许CORS 配置。

          @CrossOrigin
          @RestController
          public class SampleController { 
            .....
          }
          

          第 2 步

          Spring 已经有一个 CorsFilter,尽管您可以将自己的 CorsFilter 注册为 bean 以提供您自己的配置,如下所示。

          @Bean
          public CorsFilter corsFilter() {
              final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
              final CorsConfiguration config = new CorsConfiguration();
              config.setAllowedOrigins(Collections.singletonList("http://localhost:3000")); // Provide list of origins if you want multiple origins
              config.setAllowedHeaders(Arrays.asList("Origin", "Content-Type", "Accept"));
              config.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "OPTIONS", "DELETE", "PATCH"));
              config.setAllowCredentials(true);
              source.registerCorsConfiguration("/**", config);
              return new CorsFilter(source);
          }
          

          【讨论】:

            【解决方案7】:

            此答案复制了@abosancic 答案,但增加了额外的安全性以避免 CORS 漏洞利用

            提示 1:不要在未检查允许访问的主机列表的情况下按原样反映传入的 Origin。

            提示 2:仅允许对列入白名单的主机进行凭据请求。

            import java.io.IOException;
            import java.util.ArrayList;
            import java.util.List;
            import javax.servlet.Filter;
            import javax.servlet.FilterChain;
            import javax.servlet.FilterConfig;
            import javax.servlet.ServletException;
            import javax.servlet.ServletRequest;
            import javax.servlet.ServletResponse;
            import javax.servlet.http.HttpServletRequest;
            import javax.servlet.http.HttpServletResponse;
            import org.slf4j.Logger;
            import org.slf4j.LoggerFactory;
            import org.springframework.stereotype.Component;
            
            @Component
            public class SimpleCORSFilter implements Filter {
            
                private final Logger log = LoggerFactory.getLogger(SimpleCORSFilter.class);
            
                private List<String> allowedOrigins;
            
                public SimpleCORSFilter() {
                    log.info("SimpleCORSFilter init");
                    allowedOrigins = new ArrayList<>();
                    allowedOrigins.add("https://mysafeorigin.com");
                    allowedOrigins.add("https://itrustthissite.com");
                }
            
                @Override
                public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
            
                    HttpServletRequest request = (HttpServletRequest) req;
                    HttpServletResponse response = (HttpServletResponse) res;
            
                    String allowedOrigin = getOriginToAllow(request.getHeader("Origin"));
            
                    if(allowedOrigin != null) {
                        response.setHeader("Access-Control-Allow-Origin", allowedOrigin);
                        response.setHeader("Access-Control-Allow-Credentials", "true");
                    }
            
                    response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
                    response.setHeader("Access-Control-Max-Age", "3600");
                    response.setHeader("Access-Control-Allow-Headers", "Content-Type, Accept, X-Requested-With, remember-me");
            
                    chain.doFilter(req, res);
                }
            
                @Override
                public void init(FilterConfig filterConfig) {
                }
            
                @Override
                public void destroy() {
                }
            
                public String getOriginToAllow(String incomingOrigin) {
                    if(allowedOrigins.contains(incomingOrigin.toLowerCase())) {
                        return incomingOrigin;
                    } else {
                        return null;
                    }
                }
            }
            

            【讨论】:

              【解决方案8】:

              这对我有用。

              @EnableWebSecurity
              public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
              
                  @Override
                  protected void configure(HttpSecurity http) throws Exception {
              
                      http.cors();
                  }
              
              }
              
              @Configuration
              public class WebConfiguration implements WebMvcConfigurer {
              
                  @Override
                  public void addCorsMappings(CorsRegistry registry) {
                      registry
                          .addMapping("/**")
                          .allowedMethods("*")
                          .allowedHeaders("*")
                          .allowedOrigins("*")
                          .allowCredentials(true);
                  }
              
              }
              

              【讨论】:

                【解决方案9】:

                我正在使用spring boot 2.1.0,对我有用的是

                A.通过以下方式添加 cors 映射:

                @Configuration
                public class Config implements WebMvcConfigurer {
                    @Override
                    public void addCorsMappings(CorsRegistry registry) {
                        registry.addMapping("/**").allowedOrigins("*");
                    }
                }
                

                B.将以下配置添加到我的 HttpSecurity 以实现 spring 安全

                .cors().configurationSource(new CorsConfigurationSource() {
                
                    @Override
                    public CorsConfiguration getCorsConfiguration(HttpServletRequest request) {
                        CorsConfiguration config = new CorsConfiguration();
                        config.setAllowedHeaders(Collections.singletonList("*"));
                        config.setAllowedMethods(Collections.singletonList("*"));
                        config.addAllowedOrigin("*");
                        config.setAllowCredentials(true);
                        return config;
                    }
                })
                

                此外,在 Zuul 代理的情况下,您可以使用此 INSTEAD OF A 和 B(只需使用 HttpSecurity.cors() 在 Spring 安全性中启用它):

                @Bean
                public CorsFilter corsFilter() {
                    final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
                    final CorsConfiguration config = new CorsConfiguration();
                    config.setAllowCredentials(true);
                    config.addAllowedOrigin("*");
                    config.addAllowedHeader("*");
                    config.addAllowedMethod("OPTIONS");
                    config.addAllowedMethod("HEAD");
                    config.addAllowedMethod("GET");
                    config.addAllowedMethod("PUT");
                    config.addAllowedMethod("POST");
                    config.addAllowedMethod("DELETE");
                    config.addAllowedMethod("PATCH");
                    source.registerCorsConfiguration("/**", config);
                    return new CorsFilter(source);
                }
                

                【讨论】:

                • return new CorsFilter(source);不是这样的构造函数错误
                • @Aadam 你用的是和我一样版本的弹簧靴吗?
                • 2.1.5 正在使用中
                • @Aadam 请确保您使用的是来自org.springframework.web.filter.CorsFilter 的 CorsFilter。当我不小心从 catalina 包中使用它时,我遇到了同样的问题。
                • B 解决方案在尝试了许多解决方案后终于奏效了,谢谢 Man
                【解决方案10】:

                要构建上面的其他答案,如果您有一个带有 Spring 安全性的 Spring Boot REST 服务应用程序(不是 Spring MVC),那么通过 Spring 安全性启用 CORS 就足够了(如果您使用 Spring MVC,则使用 WebMvcConfigurer bean正如 Yogen 提到的那样,可能是 Spring 安全性将委托给其中提到的 CORS 定义的方式)

                因此,您需要有一个安全配置来执行以下操作:

                @Configuration
                @EnableWebSecurity
                public class SecurityConfig extends WebSecurityConfigurerAdapter {
                
                @Override
                protected void configure(HttpSecurity http) throws Exception {
                    //other http security config
                    http.cors().configurationSource(corsConfigurationSource());
                }
                
                //This can be customized as required
                CorsConfigurationSource corsConfigurationSource() {
                    CorsConfiguration configuration = new CorsConfiguration();
                    List<String> allowOrigins = Arrays.asList("*");
                    configuration.setAllowedOrigins(allowOrigins);
                    configuration.setAllowedMethods(singletonList("*"));
                    configuration.setAllowedHeaders(singletonList("*"));
                    //in case authentication is enabled this flag MUST be set, otherwise CORS requests will fail
                    configuration.setAllowCredentials(true);
                    UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
                    source.registerCorsConfiguration("/**", configuration);
                    return source;
                }
                

                }

                此链接有更多相关信息:https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#cors

                注意:

                1. 为 prod 部署的应用程序启用所有来源 (*) 的 CORS 可能并不总是一个好主意。
                2. 可以通过 Spring HttpSecurity 自定义启用 CSRF,没有任何问题
                3. 如果您使用 Spring 在应用程序中启用了身份验证(例如通过 UserDetailsService),则必须添加 configuration.setAllowCredentials(true);

                针对 Spring boot 2.0.0.RELEASE(即 Spring 5.0.4.RELEASE 和 Spring security 5.0.3.RELEASE)测试

                【讨论】:

                • 这解决了我的问题。作为 Spring 和 Spring Boot 的新手,我意识到我不是使用 Sring MVC 构建的。我有一个 Vue.js 客户端。其他答案似乎是针对 Spring MVC 的,但是这个答案很好地插入了我已经实现的身份验证和授权。
                • 嗨@jaletechs,我也在使用nuxtJs(一个vuejs框架)但是在设置cookie时,它不起作用。您能帮忙解决这个问题吗?
                【解决方案11】:

                如果您想在不使用过滤器或不使用配置文件的情况下启用 CORS,只需添加

                @CrossOrigin
                

                到你的控制器的顶部,它就可以工作了。

                【讨论】:

                • 采用这种方法有什么安全风险?
                • 为我工作,我尝试将标头直接添加到响应中,但由于未处理预检,因此无法正常工作。我认为这并不安全,但可能会用于某些内部应用程序。
                • 为我工作。非常方便的内部应用解决方案。
                【解决方案12】:

                对我来说,使用 spring security 时唯一能 100% 工作的事情就是跳过所有额外的过滤器和 bean 的额外绒毛,以及人们一直认为对他们有用但对我无效的任何间接“魔法”。

                而只是强制它使用普通的StaticHeadersWriter 写入您需要的标题:

                @Configuration
                @EnableWebSecurity
                public class SecurityConfig extends WebSecurityConfigurerAdapter {
                
                    @Override
                    protected void configure(HttpSecurity http) throws Exception {
                
                        http
                            // your security config here
                            .authorizeRequests()
                            .antMatchers(HttpMethod.TRACE, "/**").denyAll()
                            .antMatchers("/admin/**").authenticated()
                            .anyRequest().permitAll()
                            .and().httpBasic()
                            .and().headers().frameOptions().disable()
                            .and().csrf().disable()
                            .headers()
                            // the headers you want here. This solved all my CORS problems! 
                            .addHeaderWriter(new StaticHeadersWriter("Access-Control-Allow-Origin", "*"))
                            .addHeaderWriter(new StaticHeadersWriter("Access-Control-Allow-Methods", "POST, GET"))
                            .addHeaderWriter(new StaticHeadersWriter("Access-Control-Max-Age", "3600"))
                            .addHeaderWriter(new StaticHeadersWriter("Access-Control-Allow-Credentials", "true"))
                            .addHeaderWriter(new StaticHeadersWriter("Access-Control-Allow-Headers", "Origin,Accept,X-Requested-With,Content-Type,Access-Control-Request-Method,Access-Control-Request-Headers,Authorization"));
                    }
                }
                

                这是我发现的最直接和最明确的方式。希望它可以帮助某人。

                【讨论】:

                  【解决方案13】:

                  如果您的程序最初没有使用 Spring Security 并且无法承担更改代码的费用,那么创建一个简单的反向代理就可以解决问题。就我而言,我使用 Nginx 的配置如下:

                  http {
                    server {
                      listen 9090;
                      location / {
                        if ($request_method = 'OPTIONS') {
                        add_header 'Access-Control-Allow-Origin' '*';
                        add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
                        #
                        # Custom headers and headers various browsers *should* be OK with but aren't
                        #
                        add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
                        #
                        # Tell client that this pre-flight info is valid for 20 days
                        #
                        add_header 'Access-Control-Max-Age' 1728000;
                        add_header 'Content-Type' 'text/plain; charset=utf-8';
                        add_header 'Content-Length' 0;
                        return 204;
                        }
                        if ($request_method = 'POST') {
                        add_header 'Access-Control-Allow-Origin' '*';
                        add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
                        add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
                        add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
                        }
                        if ($request_method = 'GET') {
                        add_header 'Access-Control-Allow-Origin' '*';
                        add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
                        add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
                        add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
                        }
                  
                        proxy_pass http://localhost:8080;
                      }
                    }
                  }
                  

                  我的程序收听:8080

                  参考号:CORS on Nginx

                  【讨论】:

                    【解决方案14】:

                    扩展 WebSecurityConfigurerAdapter 类并覆盖 @EnableWebSecurity 类中的 configure() 方法将起作用:下面是示例类

                    @Override
                    protected void configure(final HttpSecurity http) throws Exception {
                    
                             http
                            .csrf().disable()
                            .exceptionHandling();
                             http.headers().cacheControl();
                    
                            @Override
                            public CorsConfiguration getCorsConfiguration(final HttpServletRequest request) {
                                return new CorsConfiguration().applyPermitDefaultValues();
                            }
                        });
                       }
                    }
                    

                    【讨论】:

                      【解决方案15】:

                      这对我有用:

                      @Configuration
                      public class MyConfig extends WebSecurityConfigurerAdapter  {
                         //...
                         @Override
                         protected void configure(HttpSecurity http) throws Exception {
                      
                             //...         
                      
                             http.cors().configurationSource(new CorsConfigurationSource() {
                      
                              @Override
                              public CorsConfiguration getCorsConfiguration(HttpServletRequest request) {
                                  CorsConfiguration config = new CorsConfiguration();
                                  config.setAllowedHeaders(Collections.singletonList("*"));
                                  config.setAllowedMethods(Collections.singletonList("*"));
                                  config.addAllowedOrigin("*");
                                  config.setAllowCredentials(true);
                                  return config;
                              }
                            });
                      
                            //...
                      
                         }
                      
                         //...
                      
                      }
                      

                      【讨论】:

                      • 虽然此代码可能会回答问题,但提供有关此代码为何和/或如何回答问题的额外上下文可提高其长期价值。
                      • 如果通过 Spring 安全启用身份验证,则 config.setAllowCredentials(true);必须放置,否则 CORS 请求仍然会失败
                      【解决方案16】:

                      检查这个:

                      @Override
                      protected void configure(HttpSecurity httpSecurity) throws Exception {
                          ...
                                  .antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
                          ...
                      }
                      

                      【讨论】:

                      • 虽然此代码可能会回答问题,但提供有关此代码为何和/或如何回答问题的额外上下文可提高其长期价值。
                      【解决方案17】:

                      我也遇到过类似的情况。经过研究和测试,这是我的发现:

                      1. 使用 Spring Boot,启用全局 CORS 的推荐方法是在 Spring MVC 中声明并结合细粒度 @CrossOrigin 配置为:

                        @Configuration
                        public class CorsConfig {
                        
                            @Bean
                            public WebMvcConfigurer corsConfigurer() {
                                return new WebMvcConfigurerAdapter() {
                                    @Override
                                    public void addCorsMappings(CorsRegistry registry) {
                                        registry.addMapping("/**").allowedMethods("GET", "POST", "PUT", "DELETE").allowedOrigins("*")
                                                .allowedHeaders("*");
                                    }
                                };
                            }
                        }
                        
                      2. 现在,由于您使用的是 Spring Security,因此您还必须在 Spring Security 级别启用 CORS,以允许它利用在 Spring MVC 级别定义的配置:

                        @EnableWebSecurity
                        public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
                        
                            @Override
                            protected void configure(HttpSecurity http) throws Exception {
                                http.cors().and()...
                            }
                        }
                        

                        Here 是非常优秀的教程,解释了 Spring MVC 框架中的 CORS 支持。

                      【讨论】:

                      • ups 它适用于此更改 http .csrf() .disable() .cors() .and()
                      • @Osgux 很高兴听到这个消息 :) 因为我使用 JWT 进行授权并且它们是 csrf 安全的,所以我没有把它放在那里 .. 如果有帮助,请不要忘记投票 :)
                      • @Marcel 你遇到了什么问题?
                      • 无法加载 :对预检请求的响应未通过访问控制检查:请求的资源上不存在“Access-Control-Allow-Origin”标头。因此,不允许访问 Origin 'localhost:8090'。
                      • @Marcel 你如何在网络配置中注册映射?如果不使用 jwt,还要确保在安全配置中禁用 csrf
                      【解决方案18】:
                      import java.io.IOException;
                      import javax.servlet.Filter;
                      import javax.servlet.FilterChain;
                      import javax.servlet.FilterConfig;
                      import javax.servlet.ServletException;
                      import javax.servlet.ServletRequest;
                      import javax.servlet.ServletResponse;
                      import javax.servlet.http.HttpServletRequest;
                      import javax.servlet.http.HttpServletResponse;
                      import org.slf4j.Logger;
                      import org.slf4j.LoggerFactory;
                      import org.springframework.stereotype.Component;
                      
                      @Component
                      public class SimpleCORSFilter implements Filter {
                      
                      private final Logger log = LoggerFactory.getLogger(SimpleCORSFilter.class);
                      
                      public SimpleCORSFilter() {
                          log.info("SimpleCORSFilter init");
                      }
                      
                      @Override
                      public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
                      
                          HttpServletRequest request = (HttpServletRequest) req;
                          HttpServletResponse response = (HttpServletResponse) res;
                      
                          response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin"));
                          response.setHeader("Access-Control-Allow-Credentials", "true");
                          response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
                          response.setHeader("Access-Control-Max-Age", "3600");
                          response.setHeader("Access-Control-Allow-Headers", "Content-Type, Accept, X-Requested-With, remember-me");
                      
                          chain.doFilter(req, res);
                      }
                      
                      @Override
                      public void init(FilterConfig filterConfig) {
                      }
                      
                      @Override
                      public void destroy() {
                      }
                      
                      }
                      

                      不需要额外定义这个过滤器,只需添加这个类。 Spring 将被扫描并为您添加。简单CORS过滤器。 示例如下:spring-enable-cors

                      【讨论】:

                      • 但是它阻止执行AuthenticationEntryPoint..请指导
                      • 非常感谢,它极大地帮助了我努力让 spring 和 ember 一起工作。干杯伙伴!
                      • FindBugs 不喜欢使用request.getHeader("Origin") 设置标题参数,如上所示,因为HTTP response splitting
                      • 如果您的应用程序中有其他过滤器,则此过滤器需要具有最高优先级,方法是使用 @Order(Ordered.HIGHEST_PRECEDENCE) 注释过滤器。
                      • 这个答案基本上绕过了拥有 CORS 的好处。通过将 Origin 标头反映到“Access-control-allow-origin”响应标头中并允许凭据请求,您只是将您的应用程序暴露给来自世界上任何站点的跨站点请求。 读者请谨慎使用此答案
                      猜你喜欢
                      • 1970-01-01
                      • 1970-01-01
                      • 2012-12-31
                      • 2020-10-24
                      • 2019-10-25
                      • 2017-11-20
                      • 2020-07-11
                      相关资源
                      最近更新 更多