什么是跨域?有哪些解决跨域的方案?

同源策略

浏览器安全策略:协议、域名、端口三者完全相同才算同源。不同源的请求默认被浏览器拦截。

跨域解决方案

1. CORS(最常用)

服务端设置响应头允许跨域:

# 简单请求
Access-Control-Allow-Origin: https://example.com
Access-Control-Allow-Credentials: true

# 预检请求响应
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Max-Age: 86400

简单请求条件(同时满足):

预检请求(OPTIONS):

// Node.js Express 示例
app.use((req, res, next) => {
  res.header('Access-Control-Allow-Origin', 'https://example.com')
  res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE')
  res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization')
  if (req.method === 'OPTIONS') {
    return res.sendStatus(200)
  }
  next()
})

2. 代理服务器

浏览器 → 同源代理服务器 → 目标服务器
// Vite 配置
export default {
  server: {
    proxy: {
      '/api': {
        target: 'http://backend.com',
        changeOrigin: true,
        rewrite: path => path.replace(/^\/api/, '')
      }
    }
  }
}

3. JSONP(已过时)

利用 <script> 标签不受同源策略限制:

function handleData(data) { console.log(data) }

const script = document.createElement('script')
script.src = 'https://api.example.com/data?callback=handleData'
document.body.appendChild(script)
// 服务器返回: handleData({ name: 'Tom' })

4. postMessage(跨窗口通信)

// 父窗口 → iframe
iframe.contentWindow.postMessage('hello', 'https://child.com')

// iframe 接收
window.addEventListener('message', (e) => {
  if (e.origin === 'https://parent.com') {
    console.log(e.data)
  }
})

5. WebSocket

WebSocket 不受同源策略限制:

const ws = new WebSocket('ws://example.com/socket')
ws.onmessage = (e) => console.log(e.data)

带凭证的跨域

fetch('https://api.example.com/data', {
  credentials: 'include'  // 携带 Cookie
})
// 服务端必须设置
// Access-Control-Allow-Origin: 具体域名(不能用 *)
// Access-Control-Allow-Credentials: true

同分类其他题目