前端 & AI 面试题库
Browser

同源策略与 CORS

什么是同源策略?什么是跨源资源共享(CORS)?

核心答案

**同源策略(Same-Origin Policy)**是浏览器的一个安全机制,限制一个源的文档或脚本如何与另一个源的资源进行交互。

同源策略

同源的定义:协议(Protocol)、域名(Domain)、端口(Port)三者完全相同。

示例

https://www.example.com:443/page.html

协议:https
域名:www.example.com
端口:443

同源:
✅ https://www.example.com:443/other.html
✅ https://www.example.com:443/api/data

不同源:
❌ http://www.example.com:443/page.html(协议不同)
❌ https://api.example.com:443/page.html(域名不同)
❌ https://www.example.com:8080/page.html(端口不同)

同源策略的限制

  1. Cookie、LocalStorage、SessionStorage:只能访问同源的数据
  2. DOM 访问:不能访问不同源的 iframe 内容
  3. AJAX 请求:不能发送跨域请求(受 CORS 限制)
  4. WebSocket:不受同源策略限制(但可以限制)

跨源资源共享(CORS)

**CORS(Cross-Origin Resource Sharing)**是一种机制,允许服务器声明哪些源可以访问其资源。

CORS 的工作原理

1. 简单请求(Simple Request)

条件

  • 方法:GET、HEAD、POST
  • 请求头:Content-Type 为 text/plainapplication/x-www-form-urlencodedmultipart/form-data
  • 没有自定义请求头

流程

浏览器 → 直接发送请求 → 服务器
浏览器 ← 检查响应头 Access-Control-Allow-Origin ← 服务器

示例

// 客户端
fetch('https://api.example.com/data');

// 服务器响应头
Access-Control-Allow-Origin: https://www.example.com

2. 预检请求(Preflight Request)

触发条件

  • 方法:PUT、DELETE、PATCH 等
  • 自定义请求头
  • Content-Type 为 application/json

流程

浏览器 → OPTIONS 预检请求 → 服务器
浏览器 ← 预检响应(CORS 头) ← 服务器
浏览器 → 实际请求 → 服务器
浏览器 ← 实际响应 ← 服务器

示例

// 客户端
fetch('https://api.example.com/data', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'X-Custom-Header': 'value'
  },
  body: JSON.stringify({ data: 'test' })
});

// 预检请求(OPTIONS)
// 服务器响应
Access-Control-Allow-Origin: https://www.example.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Content-Type, X-Custom-Header
Access-Control-Max-Age: 86400

CORS 响应头

1. Access-Control-Allow-Origin

Access-Control-Allow-Origin: https://www.example.com
Access-Control-Allow-Origin: *  # 允许所有源(不推荐,不安全)

2. Access-Control-Allow-Methods

Access-Control-Allow-Methods: GET, POST, PUT, DELETE

3. Access-Control-Allow-Headers

Access-Control-Allow-Headers: Content-Type, Authorization

4. Access-Control-Allow-Credentials

Access-Control-Allow-Credentials: true
  • 允许发送 Cookie
  • 不能与 Access-Control-Allow-Origin: * 同时使用

5. Access-Control-Max-Age

Access-Control-Max-Age: 86400
  • 预检请求的缓存时间(秒)

延伸追问

1. 如何解决跨域问题?

回答:解决方案:

1. CORS(推荐)

// 服务器设置响应头
app.use((req, res, next) => {
  res.header('Access-Control-Allow-Origin', 'https://www.example.com');
  res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
  res.header('Access-Control-Allow-Headers', 'Content-Type');
  next();
});

2. JSONP(仅 GET 请求)

// 客户端
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({ data: 'test' });

3. 代理服务器

// 开发环境:webpack devServer
module.exports = {
  devServer: {
    proxy: {
      '/api': {
        target: 'https://api.example.com',
        changeOrigin: true
      }
    }
  }
};

// 生产环境:Nginx
location /api {
  proxy_pass https://api.example.com;
}

4. postMessage(iframe 通信)

// 父页面
iframe.contentWindow.postMessage('data', 'https://child.example.com');

// 子页面
window.addEventListener('message', (event) => {
  if (event.origin === 'https://parent.example.com') {
    console.log(event.data);
  }
});

2. CORS 预检请求什么时候发送?

回答:触发条件:

1. 非简单方法

fetch('https://api.example.com/data', {
  method: 'PUT'  // 触发预检
});

2. 自定义请求头

fetch('https://api.example.com/data', {
  headers: {
    'X-Custom-Header': 'value'  // 触发预检
  }
});

3. Content-Type 为 application/json

fetch('https://api.example.com/data', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json'  // 触发预检
  },
  body: JSON.stringify({ data: 'test' })
});

不触发预检

// 简单请求
fetch('https://api.example.com/data');  // GET,不触发预检

fetch('https://api.example.com/data', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/x-www-form-urlencoded'  // 简单 Content-Type
  },
  body: 'data=test'
});

回答:设置方法:

客户端

fetch('https://api.example.com/data', {
  credentials: 'include'  // 发送 Cookie
});

服务器

Access-Control-Allow-Origin: https://www.example.com  # 不能是 *
Access-Control-Allow-Credentials: true

注意

  • Access-Control-Allow-Origin 不能是 *
  • 必须指定具体的源
  • credentials: 'include'Access-Control-Allow-Credentials: true 必须同时设置

4. CORS 错误如何排查?

回答:排查步骤:

1. 检查响应头

fetch('https://api.example.com/data')
  .then(response => {
    console.log(response.headers.get('Access-Control-Allow-Origin'));
  });

2. 查看浏览器控制台

  • 查看错误信息
  • 检查预检请求(Network 面板)
  • 查看响应头

3. 常见错误

Access to fetch at '...' from origin '...' has been blocked by CORS policy
  • 原因:服务器未设置 Access-Control-Allow-Origin
  • 解决:服务器添加 CORS 响应头
Preflight request doesn't pass access control check
  • 原因:预检请求失败
  • 解决:检查 Access-Control-Allow-MethodsAccess-Control-Allow-Headers

5. CORS 和 JSONP 的区别?

回答:主要区别:

特性CORSJSONP
支持的方法所有 HTTP 方法仅 GET
安全性高(服务器控制)低(容易受 XSS 攻击)
错误处理完善的错误处理难以处理错误
现代性现代标准传统方案
推荐度✅ 推荐❌ 不推荐

JSONP 的问题

  • 只能 GET 请求
  • 安全性差(容易受 XSS 攻击)
  • 错误处理困难
  • 需要服务器支持回调

(注:文档部分内容可能由 AI 生成)