CORS(Cross-Origin Resource Sharing)是一种网络安全协议,用于允许或限制一个域(例如example.com)的网页访问另一个域(例如api.example.com)的资源。本文介绍典型的CORS配置场景,并结合示例说明如何实现跨域资源的共享。
使用场景
在Web开发实践中,跨域请求是常见的问题,尤其是在如下场景中:
场景 | 描述 | 示例 |
前后端分离 | 前端(客户端)应用和后端(API)服务部署在不同的域或端口上。前端向后端发送XHR(XMLHttpRequest)或Fetch请求时,需要配置CORS。 | 一个前端应用程序(例如项目管理工具)部署在https://frontend.example.com,它通过API与后端服务通信,后端服务部署在https://api.example.com。 |
单页面应用(SPA) | 单页面应用程序(例如React、Vue、Angular)通过API与服务器进行通信。如果API和前端应用不在同一个域,需要配置CORS。 | 单页面应用,部署在 |
第三方服务集成 | 当需要从客户端直接请求第三方服务的API时,如果第三方服务未配置CORS,则需要通过本地服务器配置CORS来进行代理。 |
|
配置CORS
CORS机制是通过设置HTTP响应头来实现的,可以在如Nginx、Apache等Web服务器上设置,或者在后端应用代码中定义。主要涉及以下HTTP头:
名称 | 描述 | 示例 |
Access-Control-Allow-Origin | 指定哪些域可以访问资源。 |
|
Access-Control-Allow-Methods | 指定允许的方法类型,例如GET、POST、PUT等。 |
|
Access-Control-Allow-Headers | 指定允许的自定义请求头字段。 |
|
Access-Control-Allow-Credentials | 指示是否可以发送带有凭证的请求(如Cookies、HTTP认证信息)。 |
|
Access-Control-Expose-Headers | 指示哪些响应头可以在浏览器中访问。 |
|
Access-Control-Max-Age | 指定浏览器可以缓存预检请求结果的最大时间(以秒为单位)。 |
|
通过Nginx Ingress Controller配置CORS
在Kubernetes环境中,当使用Nginx Ingress控制器配置CORS策略时,我们可以通过在Ingress资源的Annotations部分添加指定注解来实现。下面的表格展示了用于CORS设置的Nginx Ingress常用注解。更多信息,请参见Enable CORS。
注释 | 描述 | 示例 |
| 启用CORS设置。 |
|
| 指定Access-Control-Allow-Origin的值。 |
|
| 指定Access-Control-Allow-Methods的值。 |
|
| 指定Access-Control-Allow-Headers的值。 |
|
| 指定Access-Control-Allow-Credentials的值。 |
|
| 指定Access-Control-Expose-Headers的值。 说明 Nginx Ingress Controller版本为v0.44及以上。 |
|
| 指定Access-Control-Max-Age的值。 |
|
示例:前后端分离场景
一个前端应用程序(例如项目管理工具)部署在https://frontend.example.com,通过API与后端服务通信,而后端服务部署在https://api.example.com。
以下示例Ingress YAML配置了CORS相关的Annotation。
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: example-ingress
namespace: default
annotations:
nginx.ingress.kubernetes.io/enable-cors: "true" # 启用CORS。
nginx.ingress.kubernetes.io/cors-allow-origin: "*" # 允许所有域访问。
nginx.ingress.kubernetes.io/cors-allow-methods: "GET, PUT, POST, DELETE, PATCH, OPTIONS" # 允许的HTTP方法。
# 允许的自定义请求头。
nginx.ingress.kubernetes.io/cors-allow-headers: "DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range"
nginx.ingress.kubernetes.io/cors-expose-headers: "Content-Length,Content-Range" # 暴露的响应头。
nginx.ingress.kubernetes.io/cors-max-age: "86400" # 预检请求缓存时间。
spec:
rules:
- host: example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: example-service
port:
number: 80
验证跨域配置是否生效
curl -X OPTIONS -H 'Origin: <来源站点>' <您配置了跨域规则所在Ingress对应的url>
如curl -X OPTIONS -H 'Origin: https://frontend.example.com' http://api.example.com/test
跨域规则会在原始后端响应下添加Access-Control-xx相关header,例如:
HTTP/1.1 200 OK
Access-Control-Allow-Origin: https://frontend.example.com
Access-Control-Allow-Methods: GET, PUT, POST, DELETE, PATCH, OPTIONS
Access-Control-Allow-Headers: DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range
Access-Control-Allow-Credentials: true
Content-Type: application/json
如果服务器响应中没有正确设置CORS头部(如Access-Control-Allow-Origin),浏览器会阻止前端 JavaScript访问响应数据,这就是所谓的CORS错误发生跨域错误对应的浏览器行为。
发送预检请求:
浏览器将CORS请求分成两类:
简单请求(simple request),直接发送请求,不需要发送预检请求。
非简单请求(not-so-simple request)。
浏览器发送预检请求:
OPTIONS /data HTTP/1.1 Host: api.example.com Origin: https://frontend.example.com Access-Control-Request-Method: POST Access-Control-Request-Headers: X-Custom-Header
预检请求的响应:
服务器响应预检请求,但未包含POST方法:
HTTP/1.1 204 No Content Access-Control-Allow-Origin: https://frontend.example.com Access-Control-Allow-Methods: GET, OPTIONS Access-Control-Allow-Headers: X-Custom-Header
浏览器决定是否发送实际请求:
浏览器检查预检请求的响应,发现
Access-Control-Allow-Methods
中不包含POST方法。浏览器会阻止后续的实际POST请求。
控制台会报类似以下的CORS错误:
Access to fetch at 'https://api.example.com/data' from origin 'https://frontend.example.com' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: xxxx.
或者
Method POST is not allowed by Access-Control-Allow-Methods in preflight response
FAQ
在设置了nginx.ingress.kubernetes.io/cors-allow-credentials: "true"
后,是否允许将nginx.ingress.kubernetes.io/cors-allow-origin
配置为"*"
?
不允许。由于浏览器安全策略限制,当Access-Control-Allow-Credentials
设置为true
时,Access-Control-Allow-Origin
不能设为通配符"*"
。这一限制旨在防止未经授权的域访问敏感资源,例如携带用户凭证信息的Cookies或者Authorization头部等。
nginx.ingress.kubernetes.io/cors-expose-headers
(或Access-Control-Expose-Headers
HTTP header)的作用对象是什么?
nginx.ingress.kubernetes.io/cors-expose-headers
用于设置Access-Control-Expose-Headers
的HTTP响应头,以决定哪些非标准响应头字段可以通过客户端的JavaScript代码获取,例如通过XMLHttpRequest
的getResponseHeader()
方法获取。
默认情况下,浏览器在处理跨域请求时只能访问标准的响应头,包括Cache-Control、Content-Language、Content-Type、Expires、Last-Modified和Pragma。服务端返回的任何自定义头字段在没有配置的情况下对客户端JavaScript都是不可见的。
通过配置Access-Control-Expose-Headers
头部,服务器可以指定哪些自定义响应头字段可以被浏览器暴露给JavaScript。例如,如果希望使自定义头X-Custom-Header
在JavaScript中可访问,服务器需在响应中添加以下头部:
Access-Control-Expose-Headers: X-Custom-Header