CORS 问题:预检响应具有无效的 HTTP 状态代码 403

Posted

技术标签:

【中文标题】CORS 问题:预检响应具有无效的 HTTP 状态代码 403【英文标题】:CORS Issue: Response for preflight has invalid HTTP status code 403 【发布时间】:2018-09-30 03:12:14 【问题描述】:

环境 Tomcat 服务器托管 web 服务应用程序。 网站是使用 SpringMVC 和 AngularJS 构建的。

核心问题: REST GET 触发 OPTIONS 请求,该请求失败。 有问题的 GET 返回一个应用程序/JSON 响应。 最初,我得到了错误:

"加载资源失败:服务器响应状态为 403 (Forbidden)

无法加载http://myServer:8084/wsPRDE/navbar/getnavbartxt:对预检请求的响应未通过访问控制检查:请求的资源上不存在“Access-Control-Allow-Origin”标头。因此,Origin 'http://myServer:8080' 不允许访问。响应的 HTTP 状态代码为 403。”

在这个问题之前,我们所做的只是在控制器中添加了@CrossOrigin。然后 httpd.conf 设置为添加这些标头。它对从站点检索的所有脚本(具有讽刺意味的是不是 CORS)进行了处理,但对实际的 CORS 响应却没有。当我确保在响应中返回 Access-Control-Allow-Origin、Allow-Methods 和 Allow-Headers 时,通过将 CORSFilter 编码到 web 服务中,错误变为:

“选项http://myServer:8084/wsPOFA/navbar/getnavbartxt403(禁止) 无法加载 http://myServer:8084/wsPOFA/navbar/getnavbartxt:预检响应包含无效的 HTTP 状态代码 403。"

背景: 错误是以一种相当随机的方式检测到的。 我的一位流程管理同事有一个 Windows 10 更新。 自从上述更新以来,整个网络服务都失败了,因为它无法访问。从站点本身到 Web 应用程序(相同地址,不同端口)的任何请求都会失败。我的上级(我是实习生)然后发现在 Chrome 和 IE 中(使用服务器别名时,而不是使用“普通”URL 时)在没有 Windows 10 更新的情况下也有同样的问题。在更新之前(或使用带有普通 URL 的 IE 时),GET 似乎不会触发 OPTIONS 请求。

进一步尝试在此之后,我尝试扩展 Allow-Headers 以包含 GET 请求/响应中存在的所有字段。我还将 OPTIONS 请求中的 Origin 与 Origins 白名单进行比较,如果它们匹配,则将 OPTIONS 响应 Allow-Origin 标头值设置为请求 Origin 值。

所以来源匹配并且请求/响应中的所有标头都设置为允许。到目前为止,我已经花了 3 天时间阅读 CORS 并在网上寻找解决方案。大多数解决方案都提到了我已经实现的 CORS 过滤器,然后有很多肮脏的解决方法,从完全清除标头到始终响应 200 OK 到 OPTIONS 请求。但它们似乎非常“肮脏”的解决方案。

谁能指出我正确的方向?我是否应该考虑将与 CORS 相关的代码添加到我的 Spring 控制器中?将 httpRequests 构建到 AngularJS 的委托是我问题的症结所在吗?还是我的想法完全错了?

目前,我的请求/响应标头如下所示:

【问题讨论】:

服务器必须响应 200 而不是 403 才能使 OPTIONS 请求成功。在此之前,它将继续失败。修改 cors 标头无助于解决此问题,它是服务器端的。 为什么你会考虑用200 作为“脏”来响应 OPTIONS 请求?这正是需要做的。 @georgeawg 好吧,也许这就是我感到困惑的地方。当请求来自 Access-Control-Allow-Origin 时,预检请求用于检查服务器是否提供 Access-Control-Allow-Method(在我的情况下为 GET)。我称其为“脏”的原因是因为在我看来,您不希望您的服务器仅在任何选项请求上回答 200。仅当来源匹配并且方法被允许时。我发现的“脏”方法是 Apache 的 RewriteValve,它只为任何 OPTIONS 请求提供 200。如果这是唯一的方法,我将不得不这样做。但我希望有一种更清洁的方法。 【参考方案1】:

这个问题被否决了,可能是因为当我决定寻求帮助时,我还没有完全清醒。无论如何,我今天设法解决了这个问题。当我发布问题时,我已经在应用程序级别制作了自定义 CORS 过滤器。如果设置了 Allow-Origin 和 Allow-Methods 字段并且仅在非选项请求上应用 chain.doFilter() 解决了问题,则更改此过滤器以手动将选项的状态代码设置为 200。我有点不确定这是否是一个理想/安全的解决方案。但它有效。您可以在下面找到使用的完整解决方案。

web.xml:

<filter>
    <filter-name>SimpleCORSFilter</filter-name>
    <filter-class>com.ofis.webservice.CORS.SimpleCORSFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>SimpleCORSFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

过滤器类:

package com.ofis.webservice.CORS;

import java.io.IOException;
import java.util.ArrayList;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class SimpleCORSFilter implements Filter 

    ArrayList<String> AllowedOrigins;
    ArrayList<String> AllowedMethods;

    @Override
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException 
    if (request.getMethod().equals("OPTIONS")) 
        String reqOrigin;
        reqOrigin = request.getHeader("Origin");

        for (String origin : AllowedOrigins) 
            if (origin.equals(reqOrigin)) 
                response.setHeader("Access-Control-Allow-Origin", reqOrigin);
                break;
            
        

        String reqMethod;
        reqMethod = request.getHeader("Access-Control-Request-Method");

        for (String method : AllowedMethods) 
            if (method.equals(reqMethod)) 
                response.setHeader("Access-Control-Allow-Method", reqMethod);
            
        

        response.setHeader("Access-Control-Max-Age", "3600");
        response.setHeader("Access-Control-Allow-Credentials", "true");
        response.setHeader("Access-Control-Allow-Headers", "cache-control,if-modified-since,pragma,Content-Type");

        //Checks if Allow-Method and Allow-Origin got set. 200 OK if both are set.
        if(!response.getHeader("Access-Control-Allow-Method").equals("") && !response.getHeader("Access-Control-Allow-Origin").equals(""))
            response.setStatus(200);
        
     else 
        chain.doFilter(req, res);
    

【讨论】:

以上是关于CORS 问题:预检响应具有无效的 HTTP 状态代码 403的主要内容,如果未能解决你的问题,请参考以下文章

Yii2 和 reactjs CORS 过滤器给出错误:预检响应具有无效的 HTTP 状态代码 401

预检响应具有无效的 HTTP 状态代码 404”

预检响应具有无效的 HTTP 状态代码 401 - Spring

CORS 问题? - 预检响应包含无效的 HTTP 状态代码 405

已启用 CORS,但在 POST JSON 时,预检响应的 HTTP 状态代码 404 无效

对 CORS 预检的响应具有 HTTP 状态代码 405