Rest POST请求没有正文时CORS失败,服务器响应为403禁止错误

Rest POST请求没有正文时CORS失败,服务器响应为403禁止错误,rest,tomcat,cors,Rest,Tomcat,Cors,我正在处理的应用程序将一个请求从运行在localhost:80上的apache服务器发送到运行在localhost:8080上的tomcat服务器,这创建了一个跨源场景。当应用程序发送带有主体的POST请求时,飞行前请求被发送,响应包含所有必要的头,然后发送实际请求,并显示成功。如果我发送相同的请求,但没有正文,则不会发送飞行前请求,并且我会收到 CORS策略已阻止从源“”访问“”处的XMLHttpRequest:请求的资源上不存在“访问控制允许源”标头 我已将CORS过滤器添加到Tomcat并

我正在处理的应用程序将一个请求从运行在localhost:80上的apache服务器发送到运行在localhost:8080上的tomcat服务器,这创建了一个跨源场景。当应用程序发送带有主体的POST请求时,飞行前请求被发送,响应包含所有必要的头,然后发送实际请求,并显示成功。如果我发送相同的请求,但没有正文,则不会发送飞行前请求,并且我会收到

CORS策略已阻止从源“”访问“”处的XMLHttpRequest:请求的资源上不存在“访问控制允许源”标头

我已将CORS过滤器添加到Tomcat并对其进行了配置:

        <filter-name>CorsFilter</filter-name>
        <filter-class>org.apache.catalina.filters.CorsFilter</filter-class>
        <init-param>
          <param-name>cors.allowed.origins</param-name>
          <param-value>http://localhost</param-value>
        </init-param>
        <init-param>
         <param-name>cors.supportedMethods</param-name>
            <param-value>GET, POST, HEAD, PUT, DELETE</param-value>
        </init-param>
        <init-param>
            <param-name>cors.support.credentials</param-name>
            <param-value>true</param-value>
        </init-param>
        <init-param>
            <param-name>cors.allowed.headers</param-name>
            <param-value>Content-Type,X-Requested-With,accept,Origin,Access-Control-Request-Method,Access-Control-Request-Headers</param-value>
        </init-param>
        <init-param>
            <param-name>cors.exposed.headers</param-name>
            <param-value>Access-Control-Allow-Origin,Access-Control-Allow-Credentials</param-value>
        </init-param>
        <init-param>
            <param-name>cors.preflight.maxage</param-name>
            <param-value>10</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>CorsFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
响应

HTTP/1.1 403 Forbidden
Server: Apache-Coyote/1.1
Access-Control-Allow-Methods: POST, GET, OPTIONS, DELETE
Access-Control-Max-Age: 3600
Access-Control-Allow-Headers: origin,x-requested-with,access-control-request-headers,content-type,access-control-request-method,accept
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Application-Context: application:dev:8080
Content-Type: text/plain;charset=UTF-8
Date: Thu, 05 Dec 2019 22:59:59 GMT
Content-Length: 0 
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Access-Control-Allow-Methods: POST, GET, OPTIONS, DELETE
Access-Control-Max-Age: 3600
Access-Control-Allow-Headers: origin,x-requested-with,access-control-request-headers,content-type,access-control-request-method,accept
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Application-Context: application:dev:8080
Access-Control-Allow-Origin: http://localhost
Access-Control-Allow-Credentials: true
Access-Control-Expose-Headers: Access-Control-Allow-Origin,Access-Control-Allow-Credentials
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Thu, 05 Dec 2019 23:08:34 GMT

b75
{"cartUUID":null}
还有一具尸体

请求

POST http://localhost:8080/webapp/api/cart/promoremove/null?school=localhost HTTP/1.1
Host: localhost:8080
Connection: keep-alive
Content-Length: 0
Accept: application/json, text/plain, */*
Origin: http://localhost
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36
Sec-Fetch-Site: same-site
Sec-Fetch-Mode: cors
Referer: http://localhost/cart-home
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Cookie: JSESSIONID=DF1A170A1B38AD7B2EFDC70A742027B8; hazelcast.sessionId=HZ90788223E83048308A167BE8514D5649; NG_TRANSLATE_LANG_KEY=%22en%22; JSESSIONID=205B812430E4261E2E2999AED6652E1D; liveagent_oref=; liveagent_ptid=4a8955a7-df01-4e22-8f77-16cabf542a11; liveagent_sid=95a17ee7-44da-480e-9f80-0fce17de4ecb; liveagent_vc=3; SESS49960de5880e8c687434170f6476605b=tumGvaRrAVNtDjAwOmMVuNivVnoZLt3muLr8KjAcyj4; ceshopCartUUID=null
POST http://localhost:8080/webapp/api/cart/promoremove/null?school=localhost HTTP/1.1
Host: localhost:8080
Connection: keep-alive
Content-Length: 15
Accept: application/json, text/plain, */*
Origin: http://localhost
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36
Content-Type: application/json;charset=UTF-8
Sec-Fetch-Site: same-site
Sec-Fetch-Mode: cors
Referer: http://localhost/cart-home
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Cookie: JSESSIONID=DF1A170A1B38AD7B2EFDC70A742027B8; hazelcast.sessionId=HZ90788223E83048308A167BE8514D5649; NG_TRANSLATE_LANG_KEY=%22en%22; JSESSIONID=205B812430E4261E2E2999AED6652E1D; liveagent_oref=; liveagent_ptid=4a8955a7-df01-4e22-8f77-16cabf542a11; liveagent_sid=95a17ee7-44da-480e-9f80-0fce17de4ecb; liveagent_vc=3; SESS49960de5880e8c687434170f6476605b=tumGvaRrAVNtDjAwOmMVuNivVnoZLt3muLr8KjAcyj4; ceshopCartUUID=null

{"some":"body"}
响应

HTTP/1.1 403 Forbidden
Server: Apache-Coyote/1.1
Access-Control-Allow-Methods: POST, GET, OPTIONS, DELETE
Access-Control-Max-Age: 3600
Access-Control-Allow-Headers: origin,x-requested-with,access-control-request-headers,content-type,access-control-request-method,accept
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Application-Context: application:dev:8080
Content-Type: text/plain;charset=UTF-8
Date: Thu, 05 Dec 2019 22:59:59 GMT
Content-Length: 0 
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Access-Control-Allow-Methods: POST, GET, OPTIONS, DELETE
Access-Control-Max-Age: 3600
Access-Control-Allow-Headers: origin,x-requested-with,access-control-request-headers,content-type,access-control-request-method,accept
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Application-Context: application:dev:8080
Access-Control-Allow-Origin: http://localhost
Access-Control-Allow-Credentials: true
Access-Control-Expose-Headers: Access-Control-Allow-Origin,Access-Control-Allow-Credentials
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Thu, 05 Dec 2019 23:08:34 GMT

b75
{"cartUUID":null}

那么,有人知道为什么在请求没有正文时不返回Allow Origin头,从而导致失败。

遇到同样的问题后,我发现Tomcat CORS筛选器实际上就是这样做的。这是CorsFilter的相关部分:

        CORSRequestType requestType = CORSRequestType.INVALID_CORS;
...
                    } else if ("POST".equals(method)) {
                        String mediaType = getMediaType(request.getContentType());
                        if (mediaType != null) {
                            if (SIMPLE_HTTP_REQUEST_CONTENT_TYPE_VALUES
                                    .contains(mediaType)) {
                                requestType = CORSRequestType.SIMPLE;
                            } else {
                                requestType = CORSRequestType.ACTUAL;
                            }
                        }
                    }
如果方法是POST,但没有内容类型标头,则请求类型仍然无效。过滤器代码中有一点下降,这将导致错误代码403

这就是导致403的原因,但我不确定这里是否正确处理了


由于我的用例是一个必须部署在多个tomcat版本上的应用程序,因此即使在某个时间点对其进行了更改/修复,我们也会依赖于这个最新的tomcat版本。这是我想要避免的,所以我要么将自己版本的CorsFilter打包到我们的应用程序中,要么更改API定义,使其发送内容类型。

遇到同样的问题后,我发现Tomcat CORS筛选器实际上就是这么做的。这是CorsFilter的相关部分:

        CORSRequestType requestType = CORSRequestType.INVALID_CORS;
...
                    } else if ("POST".equals(method)) {
                        String mediaType = getMediaType(request.getContentType());
                        if (mediaType != null) {
                            if (SIMPLE_HTTP_REQUEST_CONTENT_TYPE_VALUES
                                    .contains(mediaType)) {
                                requestType = CORSRequestType.SIMPLE;
                            } else {
                                requestType = CORSRequestType.ACTUAL;
                            }
                        }
                    }
如果方法是POST,但没有内容类型标头,则请求类型仍然无效。过滤器代码中有一点下降,这将导致错误代码403

这就是导致403的原因,但我不确定这里是否正确处理了


由于我的用例是一个必须部署在多个tomcat版本上的应用程序,因此即使在某个时间点对其进行了更改/修复,我们也会依赖于这个最新的tomcat版本。这是我想要避免的,所以我会将我自己版本的CorsFilter打包到我们的应用程序中,或者更改API定义,使其发送内容类型。

因为服务器的响应是403禁止的,默认情况下,大多数服务器系统不会将应用程序集头添加到4xx响应中。POST请求是否在
http://localhost:8080/webapp/api/cart/promoremove/null?school=localhost
定义一个强制的
请求主体
?或者它是可选的?这能回答你的问题吗@sideshowbarker-我知道标题不会添加到403响应中。问题是,为什么我在没有尸体的情况下得到403的答复,而在有尸体的情况下得到200的答复。主采购订单-此职位不需要强制机构。该应用程序目前在生产环境中工作正常,但在该环境中未执行CORS。因为服务器的响应是403禁止的,默认情况下,大多数服务器系统不会将应用程序集头添加到4xx响应中。POST请求是否在
http://localhost:8080/webapp/api/cart/promoremove/null?school=localhost
定义一个强制的
请求主体
?或者它是可选的?这能回答你的问题吗@sideshowbarker-我知道标题不会添加到403响应中。问题是,为什么我在没有尸体的情况下得到403的答复,而在有尸体的情况下得到200的答复。主采购订单-此职位不需要强制机构。该应用程序目前在生产环境中运行良好,但在该环境中没有执行CORS。