跨域请求出现preflight request失败的问题的解决

简介: # 问题出现 这两天在项目联调过程中突然前端同学报告出现CORS跨域问题无法访问。刚听到很奇怪,因为已经在项目里面设置了CORS规则,理论上不会出现这个问题。 ```java protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,

问题出现

这两天在项目联调过程中突然前端同学报告出现CORS跨域问题无法访问。刚听到很奇怪,因为已经在项目里面设置了CORS规则,理论上不会出现这个问题。

    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
                                    FilterChain filterChain) throws ServletException, IOException {
        String orignalHeader = request.getHeader("Origin");

        if (orignalHeader != null ) {
            Matcher m = CORS_ALLOW_ORIGIN_REGEX.matcher(orignalHeader);
            if (m.matches()) {
                response.addHeader("Access-Control-Allow-Origin", orignalHeader);
                response.addHeader("Access-Control-Allow-Credentials", "true");
                response.addHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, PUT");
                response.addHeader("Access-Control-Allow-Headers", "x-dataplus-csrf, Content-Type");
            }
        }
    }

拿到前端给的错误提示后发现了一个奇怪的问题,提示Response to preflight request doesn't pass access control check中的preflight request是什么?

image.png

Preflight request介绍

了解得知跨域资源共享标准新增了一组 HTTP 首部字段,允许服务器声明哪些源站通过浏览器有权限访问哪些资源。同时规范要求,对那些可能对服务器数据产生副作用的 HTTP 请求方法(特别是 GET 以外的 HTTP 请求,或者搭配某些 MIME 类型的 POST 请求),浏览器必须首先使用 OPTIONS 方法发起一个预检请求(preflight request),从而获知服务端是否允许该跨域请求。服务器确认允许之后,才发起实际的 HTTP 请求。在预检请求的返回中,服务器端也可以通知客户端,是否需要携带身份凭证(包括 Cookies 和 HTTP 认证相关数据)。

一个Preflight request的流程可以如下图所示

image.png

什么样的请求会产生Preflight request呢?当请求满足下述任一条件时,即应首先发送Preflight request请求:

  • 使用了下面任一 HTTP 方法:

    • PUT
    • DELETE
    • CONNECT
    • OPTIONS
    • TRACE
    • PATCH
  • 人为设置了对 CORS 安全的首部字段集合之外的其他首部字段。该集合为:

    • Accept
    • Accept-Language
    • Content-Language
    • Content-Type (需要注意额外的限制)
    • DPR
    • Downlink
    • Save-Data
    • Viewport-Width
    • Width
  • Content-Type 的值不属于下列之一:

    • application/x-www-form-urlencoded
    • multipart/form-data
    • text/plain
  • 请求中的XMLHttpRequestUpload 对象注册了任意多个事件监听器。
  • 请求中使用了ReadableStream对象。

在我们的例子中正是使用了POST方法传递了一个Content-Type为application/json的数据到后端。而这个OPTION请求返回失败后浏览器并没有继续下发POST请求。

解决方案

在弄清楚问题后,我们了解只要给Preflight request优先通过就可以引导后续请求继续下发。对此,我们改造CORS Filter来解决这个问题。

  • 首先对OPTION请求放入HTTP 200的响应内容。
  • 对于Preflight request询问中的的Access-Control-Request-Headers予以通过
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
                                    FilterChain filterChain) throws ServletException, IOException {
        String orignalHeader = request.getHeader("Origin");

        if (orignalHeader != null ) {
            Matcher m = CORS_ALLOW_ORIGIN_REGEX.matcher(orignalHeader);
            if (m.matches()) {
                response.addHeader("Access-Control-Allow-Origin", orignalHeader);
                response.addHeader("Access-Control-Allow-Credentials", "true");
                response.addHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, PUT");
                response.addHeader("Access-Control-Allow-Headers", request.getHeader("Access-Control-Request-Headers"));
            }
        }

        if ("OPTIONS".equals(request.getMethod())) {
            response.setStatus(HttpServletResponse.SC_OK);
        } else {
            filterChain.doFilter(request, response);
        }
    }

注意事项

但是天不遂人愿,在上述改造后理论上应该是可以解决Preflight request问题,可以测试发现依然有问题。这时我们注意到错误信息中提到的另外一句Redirect is not allowed for a preflight request.

为什么会有Redirect事情发生呢,原来所有请求在进入我们的CORS Filter之前,会首先通过SSO Filter做登录检测。而这个Preflight request并没有携带登录信息,导致OPTION请求被跳转到了登录页面。同理如果引用了Spring Security组件的的话也会出现首先被登录验证给过滤的问题。

找到问题就比较好办了,调整CORS Filter优先级,让其先于登录验证进行就好了。对此我们调整registrationBean的order从默认的Integer.MAX_VALUE到1就好了。

    @Bean(name = "corsFilter")
    public FilterRegistrationBean corsFilter() {
        FilterRegistrationBean registrationBean = new FilterRegistrationBean();
        registrationBean.setFilter(corsFilterBean());
        registrationBean.setUrlPatterns(Lists.newArrayList("/*"));
        registrationBean.setOrder(1);
        return registrationBean;
    }
目录
相关文章
|
缓存 JSON 前端开发
CORS 详解,终于不用担心跨域问题了
CORS 详解,终于不用担心跨域问题了
9531 1
CORS 详解,终于不用担心跨域问题了
|
PHP 开发工具 对象存储
PHP 使用 OSS上传文件
PHP 使用 OSS上传文件
6173 0
|
前端开发 JavaScript 应用服务中间件
前端跨域问题解决Access to XMLHttpRequest at xxx from has been blocked by CORS policy
跨域问题是前端开发中常见且棘手的问题,但通过理解CORS的工作原理并应用合适的解决方案,如服务器设置CORS头、使用JSONP、代理服务器、Nginx配置和浏览器插件,可以有效地解决这些问题。选择合适的方法可以确保应用的安全性和稳定性,并提升用户体验。
9455 90
|
应用服务中间件 API nginx
解决跨域问题 Response to preflight request doesn't pass access control check
解决跨域问题 Response to preflight request doesn't pass access control check
13388 1
|
传感器 人工智能 算法
AI技术在智慧工地中的应用有哪些?
人工智能技术(AI)通过算法和数据让计算机模拟人类智能,完成复杂任务。在智慧工地中,AI技术覆盖施工管理全流程,提升效率与安全性。主要应用包括:人员智能化管理(身份识别、行为监测)、施工安全管控(危险行为识别、设备监控、环境预警)、设备与物料管理(预测性维护、物料追溯)、施工效率与质量提升(进度调度、质量检测)及智能决策支持(大数据分析、虚拟培训)。这些技术推动建筑行业从经验驱动向数据驱动转型,助力无人化作业与全生命周期管理。
1278 0
|
存储 监控 前端开发
Sentry 监控部署与使用(详细流程)
Sentry 监控部署与使用(详细流程)
15328 1
|
前端开发 Java
前后端分离的跨域问题解决:No ‘Access-Control-Allow-Origin‘ header is present on the requested resource.
本文介绍了解决前后端分离项目中跨域问题的方法,包括添加`CorsConfig`配置类和重写`WebMvcConfigurer`接口的`addCorsMappings`方法,允许前端请求访问后端资源,并提供了具体的代码示例。
前后端分离的跨域问题解决:No ‘Access-Control-Allow-Origin‘ header is present on the requested resource.
|
前端开发 对象存储 数据安全/隐私保护
阿里云前端直传has been blocked by CORS policy: Response to preflight request doesn‘t pass access control
阿里云前端直传has been blocked by CORS policy: Response to preflight request doesn‘t pass access control
2239 2
|
安全 前端开发 JavaScript
什么是跨域?为什么会产生跨域?怎么解决跨域?
什么是跨域?为什么会产生跨域?怎么解决跨域?
3561 0