一、为什么需要处理跨域请求?
想象一下这样的场景:你的前端页面运行在https://example.com,但需要从https://api.other.com获取数据。浏览器出于安全考虑,会阻止这种"跨域"请求。这就是我们常说的"同源策略"的限制。
跨域问题就像两家不同的银行之间没有建立信任关系,A银行的卡不能在B银行的ATM机上直接取款。为了让资源能够安全共享,我们需要建立一套规则,这就是CORS(跨域资源共享)策略。
二、OpenResty中CORS的基本配置
OpenResty作为Nginx的增强版,处理CORS非常方便。我们先看一个最简单的配置示例:
# OpenResty配置示例
server {
listen 80;
server_name api.example.com;
location / {
# 允许所有域名访问(生产环境不推荐)
add_header 'Access-Control-Allow-Origin' '*';
# 允许的HTTP方法
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
# 允许的请求头
add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization';
# 预检请求缓存时间
add_header 'Access-Control-Max-Age' 1728000;
# 其他正常配置...
proxy_pass http://backend;
}
}
这个配置虽然简单,但有几个关键点需要注意:
Access-Control-Allow-Origin设置为*表示允许所有域名访问,这在开发环境很方便,但生产环境应该限制为特定域名Access-Control-Allow-Methods定义了允许的HTTP方法- 对于复杂请求(如带自定义头部的请求),浏览器会先发送OPTIONS预检请求
三、生产环境的安全配置
实际项目中,我们需要更严格的配置。下面是一个生产环境推荐的配置:
# OpenResty生产环境CORS配置
server {
listen 443 ssl;
server_name api.example.com;
# SSL证书配置
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
location / {
# 动态获取请求来源,检查是否在白名单中
set $cors "";
if ($http_origin ~* (https://example.com|https://www.example.com)) {
set $cors $http_origin;
}
# 设置CORS头
add_header 'Access-Control-Allow-Origin' $cors always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization, X-Requested-With' always;
add_header 'Access-Control-Allow-Credentials' 'true' always;
add_header 'Access-Control-Max-Age' 1728000 always;
# 处理OPTIONS预检请求
if ($request_method = 'OPTIONS') {
return 204;
}
# 其他正常配置...
proxy_pass http://backend;
}
}
这个配置有几个重要改进:
- 使用动态来源检查,只允许白名单中的域名访问
- 添加了
Access-Control-Allow-Credentials支持带凭证的请求 - 使用
always参数确保即使错误响应也包含CORS头 - 正确处理OPTIONS预检请求
四、高级场景处理
有时候我们会遇到更复杂的需求,比如:
- 需要根据不同的路由设置不同的CORS策略
- 需要记录跨域请求日志
- 需要对特定来源设置特殊权限
下面是一个处理多路由CORS的示例:
# OpenResty多路由CORS配置
server {
listen 443 ssl;
server_name api.example.com;
# 公共API - 开放给所有合作伙伴
location /public/ {
add_header 'Access-Control-Allow-Origin' '*' always;
add_header 'Access-Control-Allow-Methods' 'GET, OPTIONS' always;
if ($request_method = 'OPTIONS') {
return 204;
}
proxy_pass http://public_backend;
}
# 私有API - 仅限自己的前端使用
location /private/ {
set $cors "";
if ($http_origin = 'https://myapp.example.com') {
set $cors $http_origin;
}
add_header 'Access-Control-Allow-Origin' $cors always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS' always;
add_header 'Access-Control-Allow-Credentials' 'true' always;
if ($request_method = 'OPTIONS') {
return 204;
}
# 额外安全措施:验证来源
if ($cors = "") {
return 403;
}
proxy_pass http://private_backend;
}
# 管理API - 不允许跨域
location /admin/ {
# 不设置任何CORS头
proxy_pass http://admin_backend;
}
}
五、常见问题与解决方案
在实际使用中,你可能会遇到这些问题:
为什么设置了CORS头但还是被浏览器拦截?
- 检查是否遗漏了
OPTIONS方法处理 - 确保
Access-Control-Allow-Headers包含了所有自定义头 - 如果是带凭证的请求,
Access-Control-Allow-Origin不能是*
- 检查是否遗漏了
如何调试CORS问题?
- 使用浏览器开发者工具查看网络请求
- 检查响应头是否包含预期的CORS头
- 查看服务器日志确认请求是否到达
性能优化建议
- 合理设置
Access-Control-Max-Age减少预检请求 - 对于简单请求(GET、HEAD、POST),浏览器不会发送预检请求
- 合理设置
六、安全注意事项
配置CORS时,安全是首要考虑因素:
不要滥用通配符*
- 只在开发环境使用
Access-Control-Allow-Origin: * - 生产环境必须限制为特定域名
- 只在开发环境使用
小心敏感信息泄露
- 确保只有必要的API开放跨域访问
- 对于包含敏感信息的接口,考虑其他安全措施如JWT验证
防范CSRF攻击
- 即使配置了CORS,也要实施CSRF防护
- 对于修改数据的请求,使用CSRF token
定期审查CORS配置
- 随着业务发展,定期检查哪些API需要跨域访问
- 移除不再需要的跨域规则
七、最佳实践总结
经过以上讨论,我们可以总结出OpenResty配置CORS的最佳实践:
- 按需开放:只为确实需要跨域的API配置CORS
- 最小权限:只允许必要的HTTP方法和头
- 动态验证:使用白名单验证请求来源
- 安全加固:结合HTTPS、认证等其他安全措施
- 日志监控:记录跨域请求便于审计
记住,CORS配置不是一劳永逸的,随着业务发展和安全形势变化,需要定期审查和更新你的配置。
八、完整示例代码
最后,我们来看一个结合了所有最佳实践的完整示例:
# OpenResty CORS最佳实践配置
server {
listen 443 ssl;
server_name api.example.com;
# 域名白名单
set $cors_allowed_origins 'https://example.com https://www.example.com https://partner.example.com';
# SSL配置
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
# 全局CORS设置
location / {
# 检查请求来源是否在白名单中
set $cors_origin "";
if ($http_origin ~* ($cors_allowed_origins)) {
set $cors_origin $http_origin;
}
# 设置CORS头
add_header 'Access-Control-Allow-Origin' $cors_origin always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization, X-Request-ID' always;
add_header 'Access-Control-Allow-Credentials' 'true' always;
add_header 'Access-Control-Max-Age' 86400 always;
# 处理OPTIONS请求
if ($request_method = 'OPTIONS') {
return 204;
}
# 来源不在白名单中则拒绝
if ($cors_origin = "") {
return 403 "Forbidden - Origin not allowed";
}
# 其他安全头
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
# 代理到后端
proxy_pass http://backend;
}
# 健康检查端点 - 不开放跨域
location /health {
access_log off;
proxy_pass http://backend;
}
}
这个配置示例包含了我们讨论的所有关键点:
- 动态来源检查
- 细粒度的CORS控制
- 安全头设置
- 清晰的错误提示
- 特殊端点的例外处理
希望这篇指南能帮助你安全有效地在OpenResty中配置CORS。记住,好的安全配置应该既保护你的资源,又不给合法用户造成不必要的麻烦。
评论