Spring Boot 中的 CORS 策略冲突

Posted

技术标签:

【中文标题】Spring Boot 中的 CORS 策略冲突【英文标题】:CORS policy conflict in Spring boot 【发布时间】:2019-11-07 14:46:36 【问题描述】:

我是 Spring Boot 新手,我使用 Spring Boot 实现了一些基本的 REST API。当我尝试使用 react 调用这些 api 时,调用某些 CORS 策略时出错。然后我找到了解决该问题的方法,并且我的所有 api 端点都可以正常工作,除了登录 api 调用。我得到同样的错误调用:

这是我的网络安全 java 类。

@EnableWebSecurity
public class WebSecurity extends WebSecurityConfigurerAdapter 

    private final UserService userDetailsService;
    private final BCryptPasswordEncoder bCryptPasswordEncoder;

    public WebSecurity(UserService userDetailsService, BCryptPasswordEncoder bCryptPasswordEncoder) 
        this.userDetailsService = userDetailsService;
        this.bCryptPasswordEncoder = bCryptPasswordEncoder;
    

    @Override
    protected void configure(HttpSecurity http) throws Exception 

        http.csrf().disable().authorizeRequests().antMatchers(HttpMethod.POST, SecurityConstants.SIGN_UP_URL)
                .permitAll().anyRequest().authenticated().and().cors().and().addFilter(getAuthenticationFilter())
                .addFilter(new AuthorizationFilter(authenticationManager())).sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS);
    

    @Override
    public void configure(AuthenticationManagerBuilder auth) throws Exception 
        auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder);
    

    public AuthenticationFilter getAuthenticationFilter() throws Exception 
        final AuthenticationFilter filter = new AuthenticationFilter(authenticationManager());
        filter.setFilterProcessesUrl("/users/login");
        return filter;
    

这是我的示例控制器类。

@RestController
@RequestMapping("/users")
public class UserController 

    @Autowired
    UserService userService;

    @CrossOrigin
    @GetMapping(path = "/id", 
            produces =  MediaType.APPLICATION_XML_VALUE, MediaType.APPLICATION_JSON_VALUE )
    public UserRest getUser(@PathVariable String id) 

        UserRest returnValue = new UserRest();

        UserDto userDto = userService.getUserByUserId(id);
        BeanUtils.copyProperties(userDto, returnValue);

        return returnValue;
    

    @CrossOrigin
    @PostMapping(
            consumes =  MediaType.APPLICATION_XML_VALUE, MediaType.APPLICATION_JSON_VALUE , 
            produces = MediaType.APPLICATION_XML_VALUE, MediaType.APPLICATION_JSON_VALUE )
    public UserRest createUser(@RequestBody UserDetailsRequestModel userDetails) 

        UserRest returnValue = new UserRest();

        UserDto userDto = new UserDto();
        BeanUtils.copyProperties(userDetails, userDto);

        UserDto createUser = userService.createUser(userDto);
        BeanUtils.copyProperties(createUser, returnValue);

        return returnValue;
    

我被这个问题困住了,我真的需要一些帮助。我已经尝试过 *** 中给出的一些与我的问题类似的答案。但这并没有解决我的问题。 Mt spring boot 版本是2.1.4

【问题讨论】:

【参考方案1】:

试试这个,

@Configuration
public class WebConfiguration implements WebMvcConfigurer 

    @Override
    public void addCorsMappings(CorsRegistry registry) 
        registry.addMapping("/**")
                .allowedMethods("*");
    

更新

你也可以这样做,

@Bean
public FilterRegistrationBean corsFilter() 
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration config = new CorsConfiguration();
        config.setAllowCredentials(true);
        config.setAllowedOrigins(Collections.singletonList("*"));
        config.addAllowedHeader("*");
        config.addAllowedMethod("*");
        source.registerCorsConfiguration("/**", config);
        FilterRegistrationBean<CorsFilter> filterRegistration = new FilterRegistrationBean<>(new CorsFilter(source));
        filterRegistration.setOrder(Ordered.HIGHEST_PRECEDENCE);
        return filterRegistration;

【讨论】:

谢谢@Avi。但是我的问题解决了。顺便谢谢 解决方案是什么?【参考方案2】:

您正面临这个问题,因为您已经允许后端从端口 8080 进行 cors,但您的反应在本地端口 3000 中启动。因此,springboot 不会接受来自不同端口地址的请求。

方法一:

您可以通过使用以下注释来解决这个问题:

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

由于您使用的是 springboot ,您还可以使用以下全局配置来定义哪些所有域都可以访问您的后端。

@Configuration
public class MyConfiguration 

    @Bean
    public WebMvcConfigurer corsConfigurer() 
        return new WebMvcConfigurerAdapter() 
            @Override
            public void addCorsMappings(CorsRegistry registry) 
            registry.addMapping("/**")
                    .allowedOrigins("http://localhost:3000")
                    .allowedMethods("PUT", "DELETE", "GET", "POST") //or allow all as you like
                    .allowedHeaders("header1", "header2", "header3")
                    .exposedHeaders("header1", "header2")
                    .allowCredentials(false).maxAge(3600);
             
        ;
    

如果您仍然遇到问题,可能是因为 Spring Security 没有在您的 响应标头中添加像 Access-Control-Allow-Origin 这样的 CORS 标头。在这种情况下,您可以定义一个自定义 CORS 过滤器并将其添加到 spring 安全性,以便为所有 spring 安全性端点设置 CORS 响应标头。您可以像这样创建一个过滤器 bean:

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

    

方法二:

由于您使用的是 spring security,您还可以添加 CORS 配置以及 spring security,例如:

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter 

    @Override
    protected void configure(HttpSecurity http) throws Exception 
        http.cors().and()
            //other configurations that you want
    

    @Bean
    CorsConfigurationSource corsConfigurationSource()
    
        CorsConfiguration configuration = new CorsConfiguration();
        configuration.setAllowedOrigins(Arrays.asList("*"));
        //or any domain that you want to restrict to 
        configuration.setAllowedMethods(Arrays.asList("GET","POST"));
        //Add the method support as you like
        UrlBasedCorsConfigurationSource source = new     UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return source;
    
 

据我所知,即使您在方法 1 中提供 CORS 配置,Spring security 也会占用它。但是,在 Spring 文档中,他们也提供了这种向 Spring Security 注册 CORS 的方式。

在您附加的 控制台日志 中,据说由于返回的 http 状态不正常,飞行前检查失败。所以,您可以尝试像方法 2 一样注册 cors 并使用过滤器添加以便在响应中正确添加标题。访问登录页面时,Springboot 正在阻止飞行前检查。您需要使用 spring security 正确设置您的 cors,以允许从不同的端口访问登录页面。

确认您的登录页面 /login 映射允许 CORS

官方Doc 参考Doc

【讨论】:

我尝试了所有方法。但结果是一样的。只有登录api无法通过react访问 在您附加的控制台日志中,据说由于返回的 http 状态不正常,飞行前检查失败。所以,您可以尝试像方法 2 一样注册 cors 并添加一个过滤器标头作为响应正确添加。访问登录页面时,Springboot 正在阻止飞行前检查。您需要使用 spring security 正确设置您的 cors,以允许从不同的端口访问登录页面。 然后它说:对预检请求的响应未通过访问控制检查:请求的资源上不存在“Access-Control-Allow-Origin”标头。 在 springboot 响应头中设置以下内容,它应该可以工作:Access-Control-Allow-Origin 在我上面的回答中,我提供了如何创建自定义 CORS 过滤器,只需在您的 spring 安全配置中注册 CORS 过滤器。在这种情况下,某些浏览器(如 edge)有时不会将 * 识别为来源尝试将localhost:3000 添加到您的来源列表。

以上是关于Spring Boot 中的 CORS 策略冲突的主要内容,如果未能解决你的问题,请参考以下文章

CORS 策略错误 - 使用 Spring Boot + React 发现 api

Angular 6 + Spring Boot:错误:“来自原点 'http://localhost:4200' 已被 CORS 策略阻止”

Spring Boot中通过CORS解决跨域问题

Spring Boot:即使使用@CrossOrigin,CORS 策略也阻止了从源 FRONTEND 访问 BACK_END' 处的 XMLHttpRequest

使用 axios 从 react js 调用 Spring Boot get 方法时,浏览器显示“请求已被 CORS 策略阻止”

CORS 策略:请求的资源 Spring Boot Rest API 和 VUE 上不存在“Access-Control-Allow-Origin”标头