SpringBoot预检请求跨域问题解决方案

先说结论

增加一个过滤器类即可:

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

    private static final String OPTIONS = "OPTIONS";

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletResponse res = (HttpServletResponse) response;
        res.addHeader("Access-Control-Allow-Credentials", "true");
        res.addHeader("Access-Control-Allow-Origin", "*");
        res.addHeader("Access-Control-Allow-Methods", "OPTIONS,GET,POST,DELETE,PUT");
        res.addHeader("Access-Control-Allow-Headers", "*");
        res.addHeader("Access-Control-Max-Age", "3600");

        // 如果是OPTIONS则结束请求
        if (OPTIONS.equals(((HttpServletRequest) request).getMethod())) {
            response.getWriter().println("ok");
            return;
        }

        chain.doFilter(request, response);
    }
}

问题分析

技术说明

使用的是SpringBoot构建了一个java后端服务。

问题出现时情况

  1. 已经配置了CORS的configuration,代码如下:
@Configuration
public class CorsConfiguration implements WebMvcConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedOriginPatterns("*")
                .allowCredentials(true)
                .allowedMethods("GET", "POST", "DELETE", "PUT", "PATCH")
                .allowedHeaders("*")
                .maxAge(3600);
    }
}
  1. 对于简单请求,可以直接发送并获取信息,这要归功于CorsConfiguration,没有它简单请求也是跨域的。
  2. 但是当我自行设定了一个header之后,再发出的请求将为跨域请求,浏览器报错信息为:Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.,说的很明确了,响应缺少一个运行该源的头。
  3. 对于简单请求,响应头部有Access-Control-Allow-Origin的属性。

问题理解

  1. 网上很多都在说明什么是跨域请求,怎么个流程,这里就不再赘述了。
  2. 需要说明的是,复杂请求会先发送的这个预检请求的方法是OPTIONS,但是及时在CorsConfiguration中allowedMethods中添加该方法依然无济于事。那么此时我们应该对这个预检请求进行单独处理。
  3. 此时即有了问题结论中的过滤器类,并且我们将其声明为最高等级,遇到了OPTIONS方法直接返回并携带对应的头部。

未解决的问题

这里其实有一个很奇怪的现象,在我的这个后端实现中,对于/user路径下的接口,并不需要额外的配置即可获取复杂请求并响应,一旦不在/user路径下,相同的前端请求,相同的后端配置,就会发生跨域,这一点暂时还没有得到清晰的解决。
在添加了filter之后,所有的请求均可以正常响应了。

参考文章:

  1. SpringBoot跨域&预请求
### 解决方案 #### 方法一:全局配置CORS过滤器 为了更灵活地控制设置,可以创建一个自定义的`WebMvcConfigurer`并重写`addCorsMappings`方法。这种方式适用于整个应用程序。 ```java import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.CorsRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration public class WebConfig { @Bean public WebMvcConfigurer corsConfigurer() { return new WebMvcConfigurer() { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOrigins("http://example.com", "http://anotherdomain.com") // 允许多个源 .allowedMethods("*") // 或者指定GET, POST等特定HTTP动词 .maxAge(3600); // 预检请求的有效期为1小时 } }; } } ``` 这种方法能够覆盖所有的控制器和路径模式[^2]。 #### 方法二:使用`@CrossOrigin`注解 对于细粒度的权限管理,在具体的RESTful API接口级别上添加此注解更为合适。这使得开发人员可以直接在需要支持调用的服务端点处声明哪些客户端可以发起请求。 ```java import org.springframework.web.bind.annotation.CrossOrigin; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class MyController { @CrossOrigin(origins = {"http://localhost:8080", "https://www.example.org"}) @GetMapping("/api/data") public ResponseEntity<String> getData(){ return ResponseEntity.ok().body("{\"message\":\"This is some data\"}"); } } ``` 当涉及到IP地址而非标准名时,请注意确保格式正确无误,比如应采用`http://<ip>:<port>`的形式而不是带有额外路径的部分[^3]。 #### 处理与Spring Security集成的情况 如果项目中集成了Spring Security,则还需要调整安全配置以兼容CORS策略。通常情况下,默认的安全设定可能会阻止来自不同来源的请求通过验证流程。因此建议修改Security Config类如下: ```java @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.cors(); // 启用cors支持 super.configure(http); // 继续其他安全规则... } @Bean CorsConfigurationSource corsConfigurationSource() { final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); CorsConfiguration config = new CorsConfiguration(); config.setAllowCredentials(true); config.addAllowedOriginPattern("*"); config.setMaxAge(3600L); config.addAllowedHeader("*"); config.addAllowedMethod("*"); source.registerCorsConfiguration("/**", config); return source; } } ``` 上述代码片段展示了如何启用CORS以及如何允许凭证共享(即设置`setAllowCredentials(true)`)。请注意,开启凭证共享的同时不能使用通配符作为允许的原点列表的一部分;此时应该明确列出信任的具体站点URL[^1]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值