CORSSpringCloud后端处理跨域问题(Swagger请求报错 Failed to fetch. Possible Reasons:CORS Network Failure UR……)

注:本文仅供学习参考,如有错漏,望指正!

今天验证一个微服务框架的适用性,框架集成的Swagger发请求时提示如下异常,借此机会对CORS(跨域资源共享)了解一二:

Failed to fetch.

Possible Reasons:

  • CORS

  • Network Failure

  • URL scheme must be “http” or “https” for CORS request

关于CORS(跨域资源共享)的详细解读:

https://developer.mozilla.org/zh-CN/docs/Web/HTTP/CORS

问题记录及解决方案

1、异常截图

image-20240123170354192

2、产生异常的原因

下面这篇博文对跨域问题产生原因总结的简单易懂,直接参考↓

以下截图来自博文:【项目实战】-Spring-Cloud跨域方案汇总_springcloud跨域解决方案-CSDN博客

image-20240123170100295

3、解决方案
  • 方案一:在gateway中添加WebFilter,如(以下代码缺乏安全性,具体原因参考下文关于附带身份凭证的请求与通配符):
/**
 * CORS 过滤
 *
 * @author ahsz
 */
@Slf4j
@RequiredArgsConstructor
@Order(Ordered.HIGHEST_PRECEDENCE)
@Configuration(proxyBeanMethods = false)
public class SecurityRuleFilter implements WebFilter {

    @Override
    @SuppressWarnings("all")
    public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
        /**
         * 增加CORS
         * 解决前端登录跨域的问题
         */
        ServerHttpRequest request = exchange.getRequest();
        if (CorsUtils.isCorsRequest(request)) {
            log.info("添加CORS……");
            ServerHttpResponse response = exchange.getResponse();
            HttpHeaders headers = response.getHeaders();
            headers.add("Access-Control-Allow-Origin", "*");
            headers.add("Access-Control-Allow-Methods", "*");
            headers.add("Access-Control-Max-Age", "3600");
            headers.add("Access-Control-Allow-Headers", "*");
            // 扩展自定义header项
//            headers.add("Access-Control-Expose-Headers", "Content-Disposition");
//            if (request.getMethod() == HttpMethod.OPTIONS) {
//                response.setStatusCode(HttpStatus.OK);
//               return Mono.empty();
//            }
        }
        return chain.filter(exchange);
    }
}
  • 方案二:修改gateway网关配置信息,如下:
spring:
  cloud:
    gateway:
      globalcors:
        cors-configurations:
          '[/**]':
            allowedOrigins: "*"
            allowedHeaders: "*"
            allowedMethods: "*"
4、正常运行结果

image-20240123171216179

关于CORS的一些杂记

下文仅为重要内容,详情见:https://developer.mozilla.org/zh-CN/docs/Web/HTTP/CORS

1、关于附带身份凭证的请求与通配符

在响应附带身份凭证的请求时:

  • 服务器不能Access-Control-Allow-Origin 的值设为通配符“*”,而应将其设置为特定的域,如:Access-Control-Allow-Origin: https://example.com
  • 服务器不能Access-Control-Allow-Headers 的值设为通配符“*”,而应将其设置为标头名称的列表,如:Access-Control-Allow-Headers: X-PINGOTHER, Content-Type
  • 服务器不能Access-Control-Allow-Methods 的值设为通配符“*”,而应将其设置为特定请求方法名称的列表,如:Access-Control-Allow-Methods: POST, GET

对于附带身份凭证的请求(通常是 Cookie),

这是因为请求的标头中携带了 Cookie 信息,如果 Access-Control-Allow-Origin 的值为“*”,请求将会失败。而将 Access-Control-Allow-Origin 的值设置为 明确且安全的目标地址,则请求将成功执行。

另外,响应标头中也携带了 Set-Cookie 字段,尝试对 Cookie 进行修改。如果操作失败,将会抛出异常。

2、CORS涉及到的请求头
HTTP 响应标头字段
  • Access-Control-Allow-Origin

响应标头中可以携带一个 Access-Control-Allow-Origin字段,其语法如下:

Access-Control-Allow-Origin: <origin> | *

Access-Control-Allow-Origin 参数指定了单一的源,告诉浏览器允许该源访问资源。或者,对于不需要携带身份凭证的请求,服务器可以指定该字段的值为通配符“*”,表示允许来自任意源的请求。

例如,为了允许来自 https://mozilla.org 的代码访问资源,你可以指定:

Access-Control-Allow-Origin: https://mozilla.org
Vary: Origin

如果服务端指定了具体的单个源(作为允许列表的一部分,可能会根据请求的来源而动态改变)而非通配符“*”,那么响应标头中的 Vary 字段的值必须包含 Origin。这将告诉客户端:服务器对不同的Origin 返回不同的内容。

  • Access-Control-Expose-Headers

译者注:在跨源访问时,XMLHttpRequest 对象的 getResponseHeader()方法只能拿到一些最基本的响应头,Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma,如果要访问其他头,则需要服务器设置本响应头。Access-Control-Expose-Headers头将指定标头放入允许列表中,供浏览器的 JavaScript 代码(如 getResponseHeader()获取。

Access-Control-Expose-Headers: <header-name>[, <header-name>]*

例如:

Access-Control-Expose-Headers: X-My-Custom-Header, X-Another-Custom-Header

这样浏览器就能够通过 getResponseHeader 访问 X-My-Custom-HeaderX-Another-Custom-Header 响应头了。

  • Access-Control-Max-Age

Access-Control-Max-Age头指定了 preflight 请求的结果能够被缓存多久,请参考本文在前面提到的 preflight 例子。

Access-Control-Max-Age: <delta-seconds>

delta-seconds 参数表示 preflight 预检请求的结果在多少秒内有效。

  • Access-Control-Allow-Credentials

Access-Control-Allow-Credentials头指定了当浏览器的 credentials 设置为 true 时是否允许浏览器读取 response 的内容。当用在对 preflight 预检测请求的响应中时,它指定了实际的请求是否可以使用 credentials。请注意:简单 GET 请求不会被预检;如果对此类请求的响应中不包含该字段,这个响应将被忽略掉,并且浏览器也不会将相应内容返回给网页。

Access-Control-Allow-Credentials: true
  • Access-Control-Allow-Methods

Access-Control-Allow-Methods标头字段指定了访问资源时允许使用的请求方法,用于预检请求的响应。其指明了实际请求所允许使用的 HTTP 方法。

Access-Control-Allow-Methods: <method>[, <method>]*

有关预检请求的示例已在上方给出,包含了将此请求头发送至浏览器的示例。

  • Access-Control-Allow-Headers

Access-Control-Allow-Headers标头字段用于预检请求的响应。其指明了实际请求中允许携带的标头字段。这个标头是服务器端对浏览器端 Access-Control-Request-Headers标头的响应。

Access-Control-Allow-Headers: <header-name>[, <header-name>]*
HTTP 请求标头字段

本节列出了可用于发起跨源请求的标头字段。请注意,这些标头字段无须手动设置。当开发者使用 XMLHttpRequest对象发起跨源请求时,它们已经被设置就绪。

  • Origin

Origin 标头字段表明预检请求或实际跨源请求的源站。

Origin: <origin>

origin 参数的值为源站 URL。它不包含任何路径信息,只是服务器名称。

注: origin 的值可以为 null

注意,在所有访问控制请求中,Origin 标头字段总是被发送。

  • Access-Control-Request-Method

Access-Control-Request-Method标头字段用于预检请求。其作用是,将实际请求所使用的 HTTP 方法告诉服务器。

Access-Control-Request-Method: <method>
  • Access-Control-Request-Headers

Access-Control-Request-Headers标头字段用于预检请求。其作用是,将实际请求所携带的标头字段(通过 setRequestHeader() 等设置的)告诉服务器。这个浏览器端标头将由互补的服务器端标头 Access-Control-Allow-Headers 回答。

Access-Control-Request-Headers: <field-name>[, <field-name>]*

Over!