最近和前端联调接口遇到了比较奇怪的跨域现象,连 GET 请求都被提示跨域错误。
跨域问题现象:
我们的服务都是基于 k8s 部署,在 ingress 这层都已经设置了允许跨域:
nginx.ingress.kubernetes.io/configuration-snippet: |
# 允许来自所有来源的跨域请求。$http_origin 是一个变量,表示请求的来源地址。
more_set_headers "Access-Control-Allow-Origin: $http_origin";
# 允许跨域请求携带身份凭证(如 cookies)
more_set_headers 'Access-Control-Allow-Credentials: true';
# 设置了允许的HTTP请求方法。
more_set_headers 'Access-Control-Allow-Methods: GET,POST,PUT,DELETE,PATCH,OPTIONS';
# 这行代码允许所有类型的HTTP头部。
more_set_headers 'Access-Control-Allow-Headers: *';
# 这段代码用于处理预检请求(OPTIONS请求)。如果请求方法是OPTIONS,直接返回204状态码(无内容),并结束请求。
if ($request_method = 'OPTIONS') {
return 204;
}
通过排查 pod 和 ingress 的日志,发现 pod 服务并没有收到请求,则可能是 ingress 这段配置有些问题,通过上网搜索对上面的配置进行了优化。
解决:
在处理 OPTIONS 请求时又设置了一些头信息。改完之后的配置:
nginx.ingress.kubernetes.io/configuration-snippet: |
if ($request_method = 'OPTIONS') {
# 指定了预检请求( OPTIONS 请求)的结果可以缓存多久
more_set_headers 'Access-Control-Max-Age: 3600';
more_set_headers 'Access-Control-Allow-Origin: $http_origin';
more_set_headers 'Access-Control-Allow-Credentials: true';
more_set_headers 'Access-Control-Allow-Methods: *';
more_set_headers 'Access-Control-Allow-Headers: *';
return 204;
}
more_set_headers 'Access-Control-Allow-Origin: $http_origin';
more_set_headers 'Access-Control-Allow-Credentials: true';
more_set_headers 'Access-Control-Allow-Methods: *';
more_set_headers 'Access-Control-Allow-Headers: *';
使用上面的配置就解决了跨域报错问题。通过本次问题的解决过程,又重新复习一下跨域的知识。
跨域概念
跨域(Cross-Origin Resource Sharing, CORS)是指在浏览器中,当一个网页从一个域名(origin)向另一个域名请求资源时,由于安全原因,浏览器会限制这些请求。这种限制被称为“同源策略”,它是为了防止恶意网站读取另一个网站的敏感信息。
跨域是指当一个网页试图从不同的域、协议或端口请求资源时,浏览器会阻止这些请求。例如:
从 http://example.com 向 http://api.example.com 请求资源
从 http://example.com 向 https://example.com 请求资源
从 http://example.com 向 http://example.com 请求资源
为了解决跨域问题,可以使用 CORS 机制。通常在 nginx 或者后端服务配置CORS规则,通过设置 HTTP 头来告诉浏览器允许哪些跨域请求。常用的 CORS 头包括:
Access-Control-Allow-Origin:指定允许访问资源的域名。
Access-Control-Allow-Methods:指定允许的 HTTP 方法(例如,GET、POST)。
Access-Control-Allow-Headers:指定允许的 HTTP 头信息。
Access-Control-Allow-Credentials:指示是否允许发送 Cookie。
Access-Control-Max-Age:指定预检请求的结果可以缓存多长时间。
简单请求和复杂请求
在跨域资源共享(CORS)中,根据请求的复杂程度,浏览器将跨域请求分为简单请求和复杂请求。
简单请求是指满足以下条件的跨域请求:
请求方法:
GET
HEAD
POST
请求头信息(除了自动设置的头信息外,不能包含其他自定义头信息):
Accept
Accept-Language
Content-Language
Content-Type(仅限于以下三种内容类型)
text/plain
multipart/form-data
application/x-www-form-urlencoded
对于简单请求,浏览器会自动添加一个 Origin 头信息,表示请求的来源。当服务器收到请求后,如果允许跨域访问,则在响应头中添加相应的 CORS 头信息:
Access-Control-Allow-Origin
Access-Control-Allow-Credentials(如果需要允许发送凭证,如 Cookies)
复杂请求是不满足简单请求条件的跨域请求。对于复杂请求,浏览器会在实际请求之前发送一个预检请求(preflight request)。预检请求使用 OPTIONS 方法,目的是询问服务器是否允许实际请求。
预检请求包含以下请求头信息:
Origin:请求的来源。
Access-Control-Request-Method:实际请求将使用的 HTTP 方法。
Access-Control-Request-Headers:实际请求将使用的自定义头信息。
如果服务器允许请求,则返回带有适当头信息的响应,并且浏览器会继续发送实际请求。否则,浏览器将阻止实际请求。
简单来说:
简单请求:满足特定条件(方法和头信息)的跨域请求,直接发送,不需要预检请求。
复杂请求:不满足简单请求条件的跨域请求,浏览器会先发送预检请求,以确定服务器是否允许实际请求。
总结此次跨域错误排查的经历,我们不仅复习了跨域相关的知识,还系统地探讨了跨域问题的成因及其解决方法,并详细介绍了简单请求和复杂请求的区别和处理方式。希望这些内容对你有所帮助。