跳到主要内容

跨域配置

背景介绍

链路传播协议允许在分布式系统中传递跟踪信息,通常通过在 HTTP 请求头中添加特殊的请求头来传递 traceId 和 spanId 等跟踪信息。

跨域资源共享 (CORS)

CORS(跨域资源共享)是一种机制,它使用附加的 HTTP 头来告诉浏览器让运行在一个来源(domain)的 Web 应用被准许访问另一个来源的资源。为了确保安全性,浏览器会对某些跨域请求(特别是带有自定义头的请求)执行预检请求(Preflight Request)。 image.png

预检请求(Preflight Request)

当使用 XMLHttpRequest 或 Fetch API 进行跨域请求时,如果请求使用了以下之一,则浏览器会先发送一个 OPTIONS 请求,称为预检请求,以检查服务器是否允许该实际请求:

  1. 使用了除 GET、POST 或 HEAD 之外的 HTTP 方法。
  2. 使用了自定义的请求头(即不是 CORS 规范中列出的标准头,如 Content-Type、Accept、Authorization 等)。

由于 OpenTelemetry 需要在请求头中添加自定义的跟踪信息(如 traceparent、tracestate),这会触发浏览器的预检请求。

示例

我们有一个听云跟踪头 X-Tingyun,其格式如下:

X-Tingyun:c=B|b1t76Pq_FnI;x=4d42de860dc64124;u=base64#c3p3YW5zaGVuZw==

当浏览器发起包含此请求头的跨域请求时,会首先发送一个预检请求:

OPTIONS /api/resource HTTP/1.1
Host: api.example.com
Origin: http://example.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: X-Tingyun

服务器需要响应允许该请求:

HTTP/1.1 204 No Content
Access-Control-Allow-Origin: http://example.com
Access-Control-Allow-Methods: POST
Access-Control-Allow-Headers: X-Tingyun

如果服务器允许预检请求,浏览器会发送实际的请求:

POST /api/resource HTTP/1.1
Host: api.example.com
Origin: http://example.com
X-Tingyun:c=B|b1t76Pq_FnI;x=4d42de860dc64124;u=base64#c3p3YW5zaGVuZw==

配置说明

NGINX 配置示例

假设您的后端服务在 NGINX 之后运行。我们需要配置 NGINX 以处理预检请求并允许自定义请求头。

server {
listen 80;
server_name api.example.com;

# 定义一个位置块来处理预检请求
location /api/ {
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'X-Tingyun';
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Length' 0;
add_header 'Content-Type' 'text/plain charset=UTF-8';
return 204;
}
# 处理实际请求
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Expose-Headers' 'X-Tingyun-Data';

proxy_pass http://backend_service;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}

配置说明

  1. 处理预检请求:在 location /api/ 块中,我们首先检查请求方法是否为 OPTIONS。如果是,则添加 CORS 相关的响应头,并返回 204 状态码。
  2. 添加响应头:对于所有请求(包括预检请求和实际请求),我们都添加了 Access-Control-Allow-OriginAccess-Control-Allow-MethodsAccess-Control-Allow-Headers 头。这些头允许跨域请求并允许包含自定义头。
  3. Access-Control-Max-Age:用于配置预检请求的缓存时间,避免每次请求都发送预检请求。
  4. 'Access-Control-Expose-Headers' 'X-Tingyun-Data':该请求头用于生成WEB SDK可以获取返回性能数据,按固定格式配置即可。

Apache httpd 配置示例

开启mod_headers和mod_rewrite模块

在Apache中,CORS相关的头部设置需要依赖于mod_headers和mod_rewrite模块。首先,你需要确保该模块已经启用。在httpd.conf文件中找到以下行,并确保没有被注释掉。

LoadModule headers_module modules/mod_headers.so
LoadModule rewrite_module modules/mod_rewrite.so

配置跨域设置

<Location /api/>	  
# 处理OPTIONS预检请求
<IfModule mod_headers.c>
SetEnvIf Request_Method OPTIONS preflight
Header add Access-Control-Allow-Methods "GET, POST, OPTIONS" env=preflight
Header add Access-Control-Allow-Headers "X-Tingyun" env=preflight
Header add Access-Control-Max-Age 1728000 env=preflight

# 对OPTIONS请求返回204状态码
RewriteCond %{REQUEST_METHOD} OPTIONS
RewriteRule ^ - [R=204,L]
</IfModule>

# 对于其他请求,设置CORS头部
Header add Access-Control-Allow-Origin "*"
Header add Access-Control-Expose-Headers "X-Tingyun-Data"
</Location>

测试和验证

测试预检请求

使用工具(如 curl 或 Postman)发送一个带有自定义头的 OPTIONS 请求,确保服务器返回正确的 CORS 头。

curl -X OPTIONS -H "Origin: http://example.com" -H "Access-Control-Request-Method: POST" -H "Access-Control-Request-Headers: X-Tingyun" http://api.example.com/api/resource

实际请求

确认预检请求通过后,发送实际请求以验证所有头信息被正确处理。

curl -X POST -H "Origin: http://example.com" -H "c=B|b1t76Pq_FnI;x=4d42de860dc64124;u=base64#c3p3YW5zaGVuZw==" -H "Content-Type: application/json" -d '{"data": "example"}' http://api.example.com/api/resource

通过以上配置,NGINX 可以正确处理包含 听云请求头的跨域请求,同时符合 CORS 规范。

配置指南

Web SDK支持多个厂家和自定义链路传播请求头,所以需要按需配置

获取须配置的请求头

访问菜单Web/应用/设置/链路追踪,找到查看须配置的请求头点击复制弹出的请求头进行对应的跨域配置 image.png image.png

nginx示例配置

server {
listen 80;
server_name api.example.com;

# 定义一个位置块来处理预检请求
location /api/ {
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'X-Tingyun traceparent, tracestate';
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Length' 0;
add_header 'Content-Type' 'text/plain charset=UTF-8';
return 204;
}
# 处理实际请求
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Expose-Headers' 'X-Tingyun-Data';

proxy_pass http://backend_service;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}

以上配置声明了听云 w3c-context两个厂商的链路传播协议

附录

https://developer.mozilla.org/zh-CN/docs/Web/HTTP/CORS
https://opentelemetry.io/docs/concepts/context-propagation/
https://www.w3.org/TR/trace-context/