1、同源与非同源
2、同源请求与非同源请求
3、浏览器会对跨域做出哪些限制?
4.解决跨域/异源问题的几种方案
解决跨域或异源问题(Cross-Origin Resource Sharing, CORS)是Web开发中常见的需求,主要涉及到浏览器的同源策略。同源策略限制了来自不同源的文档或脚本如何与当前文档交互。以下是几种常见的解决跨域问题的方法:
-
JSONP(JSON with Padding):
- 利用
<script>
标签不受同源策略限制的特性,通过动态创建<script>
标签并设置其src
属性来请求跨域资源。 - 服务端返回的是一个调用函数的字符串,而不是纯JSON数据。
- 利用
-
CORS(Cross-Origin Resource Sharing):
- 通过服务器设置响应头
Access-Control-Allow-Origin
来允许特定的外部域访问资源。 - 可以设置
Access-Control-Allow-Methods
来限制允许的HTTP方法。 - 还可以设置
Access-Control-Allow-Headers
来允许特定的请求头。
- 通过服务器设置响应头
-
代理服务器:
- 在同源策略限制的服务器和需要访问的服务器之间设置一个代理服务器。
- 客户端的请求首先发送到代理服务器,代理服务器再转发到目标服务器,然后将响应返回给客户端。
-
Document.domain:
- 适用于两个不同域但同主域的页面,例如
example.com
和sub.example.com
。 - 通过设置
document.domain
为相同的主域来绕过同源策略。
- 适用于两个不同域但同主域的页面,例如
-
Window.postMessage:
- 允许不同源的iframe之间安全地传递消息。
- 使用
window.postMessage
方法发送消息,并通过监听message
事件来接收消息。
-
WebAssembly:
- 利用WebAssembly的模块加载特性,可以在不同源的上下文中加载和执行代码。
-
Server-Sent Events (SSE):
- 允许服务器向客户端发送新数据,但只支持单向通信(从服务器到客户端)。
- SSE不受同源策略的限制。
-
WebSockets:
- 允许开启一个全双工通信会话,可以在不受同源策略限制的情况下进行数据交换。
-
Service Workers:
- 通过Service Workers可以拦截网络请求并进行处理,从而实现跨域请求。
不伤代码是不是看着很呆,下面是以上方案做出的代码示例:
-
JSONP:
HTML:<script src="https://api.example.com/data?callback=myCallback"></script>
JavaScript:
function myCallback(data) { console.log(data); // 处理数据 }
Server-side (伪代码):
def jsonp_handler(request): data = get_data() # 获取数据 callback = request.args.get('callback', 'callback') # 获取回调函数名 return callback + '(' + json.dumps(data) + ');'
-
CORS:
Server-side (Python Flask示例):from flask import Flask, jsonify app = Flask(__name__) @app.route('/data') def data(): response = jsonify({'data': 'Here is your data'}) response.headers['Access-Control-Allow-Origin'] = '*' # 允许所有域 return response
-
代理服务器:
Node.js代理示例:const http = require('http'); const proxy = http.createServer((req, res) => { const target = 'http://api.example.com'; // 目标服务器 const options = { target: target, changeOrigin: true, pathRewrite: {'^/api': ''} }; const proxyRequest = http.request(options, proxyRes => { res.writeHead(proxyRes.statusCode, proxyRes.headers); proxyRes.pipe(res); }); req.pipe(proxyRequest); }); proxy.listen(8080, () => { console.log('Proxy server listening on port 8080'); });
-
Document.domain:
HTML (两个页面):<!-- 页面1 --> <script> document.domain = 'example.com'; window.data = { message: 'Hello' }; </script> <!-- 页面2 --> <script> document.domain = 'example.com'; console.log(window.data.message); // 'Hello' </script>
-
Window.postMessage:
HTML (iframe):<iframe id="myIframe" src="http://other-domain.com/page.html"></iframe> <script> document.getElementById('myIframe').contentWindow.postMessage('Hello', 'http://other-domain.com'); </script>
JavaScript (在iframe中):
window.addEventListener('message', function(event) { console.log('Received message:', event.data); // 确保消息来自可信源 if (event.origin === 'http://calling-domain.com') { console.log('Message from trusted source'); } });
-
WebAssembly:
由于WebAssembly的复杂性,这里不提供具体代码示例,但基本原理是使用fetch
或XMLHttpRequest
从其他源加载WASM模块,然后使用WebAssembly的API来编译和实例化模块。 -
Server-Sent Events (SSE):
HTML:<script> var source = new EventSource('http://api.example.com/events'); source.onmessage = function(event) { console.log(event.data); }; </script>
Server-side (Node.js示例):
const http = require('http'); const server = http.createServer((req, res) => { res.writeHead(200, { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache', 'Connection': 'keep-alive' }); setInterval(() => { res.write(`data: ${JSON.stringify({ time: new Date() })}\n\n`); }, 1000); }); server.listen(8080);
-
WebSockets:
HTML:<script> var ws = new WebSocket('ws://api.example.com/socket'); ws.onmessage = function(event) { console.log('Message from server ', event.data); }; </script>
Server-side (Node.js示例使用
ws
库):const WebSocket = require('ws'); const server = new WebSocket.Server({ port: 8080 }); server.on('connection', function(socket) { console.log('Client connected'); socket.on('message', function(message) { console.log('Received message: ', message); }); });
-
Service Workers:
self.addEventListener('fetch', function(event) { event.respondWith( fetch(event.request).catch(function() { return fetch('http://api.example.com/fallback'); }) ); });