Skip to content

浏览器同源策略和跨域解决方案

同源策略

两个源的URL在 协议、域名、端口 三个方面完全相同才被认为是同源

跨域

跨域是浏览器的同源策略导致的安全限制问题。当前端应用和后端 API 不在同一个 "源" 时,浏览器会阻止它们之间的直接通信

受同源策略限制的场景

shell
1. AJAX 请求
2. <script> || <iframe> 标签内的内容依然受跨域限制(标签是可以跨域的)
3. Web Workers
4. Web字体
5. 读取非同源的Cookie、LocalStorage等

不会限制跨域的场景

shell
标签
1. <script> || <iframe> 标签本身,标签内的内容依然受跨域限制
2. <img>, <video>, <audio> 等媒体标签
3. <link rel="stylesheet"> CSS 文件
4. <object>, <embed>, <applet>
    
WebSocket 协议
postMessage
document.domain

解决跨域的几种方式

方式一:CORS (Cross-Origin Resource Sharing):服务端设置请求头,允许服务器声明哪些源可以访问它的资源 方式二:代理服务器:Nginx反向代理 或者 Node.js中间件代理:利用在服务器上不存在同源策略一说 方式三:JSONP (JSON with Padding):script标签中发送请求,在window.success()函数的回调中接收参数 方式四:开发环境可以通过构建工具的 proxy 代理配置,解决开发环境跨域问题

示例:cors 跨域

  1. 流程:浏览器发送 OPTIONS 方法的预检请求 --> 服务器响应预检请求 --> 如果预检通过,浏览器发送实际请求 --> 服务器返回实际响应
  2. 关键请求头
shell
# 客户端设置的请求头
Origin:请求源
Access-Control-Request-Method:实际请求将使用的方法
Access-Control-Request-Headers:实际请求将携带的自定义头部
fetch('https://api.example.com/data', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer token123'
  },
})

# 服务端设置允许的请求头
Access-Control-Allow-Origin:指定允许访问资源的源
Access-Control-Allow-Methods:允许的HTTP方法
Access-Control-Allow-Headers:允许的请求头
Access-Control-Allow-Credentials:是否允许发送Cookie
Access-Control-Max-Age:预检请求的缓存时间
Access-Control-Expose-Headers:允许浏览器访问的响应头
app.use((req, res, next) => {
  // 允许所有源访问
  res.header('Access-Control-Allow-Origin', '*');
  // 或指定特定源
  // 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');
  
  // 允许携带凭证(如cookies)
  // res.header('Access-Control-Allow-Credentials', 'true');
  
  // 预检请求直接返回200
  if (req.method === 'OPTIONS') {
    return res.sendStatus(200);
  }
  next();
});

注意

  1. 前后端分离的项目肯定会出现跨域
  2. 同源策略是浏览器独有的,在服务器上不存在同源策略一说,所以可以使用代理服务器实现跨域