找回密码
 立即注册
首页 业界区 业界 跨域问题排查实战:一个困扰两天的线上问题 ...

跨域问题排查实战:一个困扰两天的线上问题

表弊捞 2025-6-6 18:45:46
"老师,我们的新功能上线后接口突然调不通了!"周一早上,实习生小李急匆匆地跑来找我。我打开监控面板,发现生产环境的错误日志突然暴增,全是 CORS 相关的报错。作为技术导师,我立即和小李一起开始排查这个问题。
说实话,跨域问题在本地开发时很常见,但在生产环境突然出现还是第一次。更让人困惑的是,这些接口在上周五还是好好的,周末发版后就出问题了。带着这些疑问,我们开始了一场"破案"之旅。
问题的表象

首先,我让小李演示了一下具体的错误场景。在浏览器控制台里,我们看到了这样的报错:
  1. Access to XMLHttpRequest at 'https://api.example.com/data'
  2. from origin 'https://www.example.com' has been blocked by CORS policy:
  3. No 'Access-Control-Allow-Origin' header is present on the requested resource.
复制代码
"奇怪了,我们的 CORS 配置明明一直都在啊。"小李一脸困惑。我们开始仔细梳理上周的发版内容:
  1. // 上周的发版记录
  2. const deployChanges = {
  3.   frontend: {
  4.     'feat: 新增数据分析页面': {
  5.       api: 'https://api-analysis.example.com/v1',
  6.       changes: ['新增数据大屏', '接入实时数据接口', '对接新的认证服务']
  7.     }
  8.   },
  9.   backend: {
  10.     'refactor: 服务架构调整': {
  11.       changes: ['网关服务升级', '认证服务重构', '引入服务网格']
  12.     }
  13.   }
  14. }
复制代码
深入排查

通过对比测试环境和生产环境的请求,我们发现了一些线索:
  1. // 测试环境的请求(正常)
  2. fetch('https://api-test.example.com/data', {
  3.   headers: {
  4.     Authorization: 'Bearer token123',
  5.     'Content-Type': 'application/json'
  6.   }
  7. }).then(response => {
  8.   console.log('响应头:', response.headers)
  9.   // Access-Control-Allow-Origin: https://www.example.com
  10.   // Access-Control-Allow-Methods: GET, POST, OPTIONS
  11.   // Access-Control-Allow-Headers: Content-Type, Authorization
  12. })
  13. // 生产环境的请求(异常)
  14. fetch('https://api.example.com/data', {
  15.   headers: {
  16.     Authorization: 'Bearer token123',
  17.     'Content-Type': 'application/json'
  18.   }
  19. }).then(response => {
  20.   console.log('响应头:', response.headers)
  21.   // 缺少 CORS 相关的响应头
  22. })
复制代码
通过进一步分析,我们发现问题出在新引入的服务网格配置上:
  1. # 原来的网关配置
  2. apiVersion: networking.istio.io/v1alpha3
  3. kind: VirtualService
  4. metadata:
  5.   name: api-gateway
  6. spec:
  7.   hosts:
  8.     - 'api.example.com'
  9.   gateways:
  10.     - api-gateway
  11.   http:
  12.     - route:
  13.         - destination:
  14.             host: api-service
  15.             port:
  16.               number: 80
  17.       # 这里缺少了 CORS 策略配置
复制代码
问题的根源

原来是这样!在服务架构调整时,我们将原来网关层的 CORS 配置迁移到了服务网格,但是漏掉了一些细节:

  • 预检请求(OPTIONS)没有正确配置
  • 多级域名的跨域配置缺失
  • 认证头信息没有加入允许列表
解决方案

知道问题后,解决方案就比较清晰了:
  1. # 修复后的配置
  2. apiVersion: networking.istio.io/v1alpha3
  3. kind: VirtualService
  4. metadata:
  5.   name: api-gateway
  6. spec:
  7.   hosts:
  8.     - 'api.example.com'
  9.     - 'api-analysis.example.com'
  10.   gateways:
  11.     - api-gateway
  12.   http:
  13.     - corsPolicy:
  14.         allowOrigins:
  15.           - exact: 'https://www.example.com'
  16.           - regex: 'https://*.example.com'
  17.         allowMethods:
  18.           - GET
  19.           - POST
  20.           - OPTIONS
  21.         allowHeaders:
  22.           - Authorization
  23.           - Content-Type
  24.         maxAge: '24h'
  25.       route:
  26.         - destination:
  27.             host: api-service
  28.             port:
  29.               number: 80
复制代码
同时,我们在前端也增加了错误处理机制:
  1. // utils/request.ts
  2. class APIClient {
  3.   private async request(url: string, options: RequestOptions) {
  4.     try {
  5.       const response = await fetch(url, {
  6.         ...options,
  7.         headers: {
  8.           ...options.headers,
  9.           'Content-Type': 'application/json'
  10.         }
  11.       })
  12.       if (!response.ok) {
  13.         // 处理 HTTP 错误
  14.         throw new HTTPError(response.status, response.statusText)
  15.       }
  16.       return await response.json()
  17.     } catch (error) {
  18.       if (error instanceof HTTPError) {
  19.         // 处理 HTTP 错误
  20.         if (error.status === 0) {
  21.           console.error('可能是跨域问题:', error)
  22.           // 显示友好的错误提示
  23.           notification.error({
  24.             message: '网络请求失败',
  25.             description: '请检查网络连接或联系技术支持'
  26.           })
  27.         }
  28.       }
  29.       throw error
  30.     }
  31.   }
  32.   // 提供重试机制
  33.   async requestWithRetry(url: string, options: RequestOptions, retries = 3) {
  34.     for (let i = 0; i < retries; i++) {
  35.       try {
  36.         return await this.request(url, options)
  37.       } catch (error) {
  38.         if (i === retries - 1) throw error
  39.         // 延迟重试
  40.         await new Promise(resolve => setTimeout(resolve, 1000 * Math.pow(2, i)))
  41.       }
  42.     }
  43.   }
  44. }
复制代码
预防措施

为了防止类似问题再次发生,我们建立了一套完整的测试机制:
  1. // tests/cors.spec.ts
  2. describe('CORS Configuration Tests', () => {
  3.   const origins = ['https://www.example.com', 'https://admin.example.com', 'https://data.example.com']
  4.   const endpoints = ['/api/v1/data', '/api/v1/analysis', '/api/v1/auth']
  5.   origins.forEach(origin => {
  6.     endpoints.forEach(endpoint => {
  7.       it(`should allow CORS from ${origin} to ${endpoint}`, async () => {
  8.         const response = await fetch(`https://api.example.com${endpoint}`, {
  9.           method: 'OPTIONS',
  10.           headers: {
  11.             Origin: origin,
  12.             'Access-Control-Request-Method': 'POST',
  13.             'Access-Control-Request-Headers': 'Content-Type,Authorization'
  14.           }
  15.         })
  16.         expect(response.headers.get('Access-Control-Allow-Origin')).to.include(origin)
  17.         expect(response.headers.get('Access-Control-Allow-Methods')).to.include('POST')
  18.         expect(response.headers.get('Access-Control-Allow-Headers')).to.include('Authorization')
  19.       })
  20.     })
  21.   })
  22. })
复制代码
经验总结

这次问题排查让我们学到了很多:

  • 架构 设计 更要特别注意配置迁移的完整性
  • 跨域配置要考虑全面,包括预检请求和各种场景
  • 要建立完善的测试机制,及早发现问题
  • 前端要有合适的错误处理机制
就像搬家时要仔细检查有没有遗漏重要物品一样,系统架构调整时也要特别注意配置的完整性。而且要像检查清单一样,把所有可能的场景都测试一遍。
写在最后

跨域问题虽然常见,但解决起来并不简单,特别是在复杂的微服务架构中。关键是要理解背后的原理,建立完善的测试机制,这样才能及时发现和解决问题。
有什么问题欢迎在评论区讨论,让我们一起提高技术水平!
如果觉得有帮助,别忘了点赞关注,我会继续分享更多实战经验~

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
您需要登录后才可以回帖 登录 | 立即注册