搞懂跨域+解决方案

1、同源与非同源
在这里插入图片描述

2、同源请求与非同源请求
在这里插入图片描述
3、浏览器会对跨域做出哪些限制?
3.1 限制DOM访问
在这里插入图片描述

4.解决跨域/异源问题的几种方案
解决跨域或异源问题(Cross-Origin Resource Sharing, CORS)是Web开发中常见的需求,主要涉及到浏览器的同源策略。同源策略限制了来自不同源的文档或脚本如何与当前文档交互。以下是几种常见的解决跨域问题的方法:

  1. JSONP(JSON with Padding)

    • 利用<script>标签不受同源策略限制的特性,通过动态创建<script>标签并设置其src属性来请求跨域资源。
    • 服务端返回的是一个调用函数的字符串,而不是纯JSON数据。
  2. CORS(Cross-Origin Resource Sharing)

    • 通过服务器设置响应头Access-Control-Allow-Origin来允许特定的外部域访问资源。
    • 可以设置Access-Control-Allow-Methods来限制允许的HTTP方法。
    • 还可以设置Access-Control-Allow-Headers来允许特定的请求头。
  3. 代理服务器

    • 在同源策略限制的服务器和需要访问的服务器之间设置一个代理服务器。
    • 客户端的请求首先发送到代理服务器,代理服务器再转发到目标服务器,然后将响应返回给客户端。
  4. Document.domain

    • 适用于两个不同域但同主域的页面,例如example.comsub.example.com
    • 通过设置document.domain为相同的主域来绕过同源策略。
  5. Window.postMessage

    • 允许不同源的iframe之间安全地传递消息。
    • 使用window.postMessage方法发送消息,并通过监听message事件来接收消息。
  6. WebAssembly

    • 利用WebAssembly的模块加载特性,可以在不同源的上下文中加载和执行代码。
  7. Server-Sent Events (SSE)

    • 允许服务器向客户端发送新数据,但只支持单向通信(从服务器到客户端)。
    • SSE不受同源策略的限制。
  8. WebSockets

    • 允许开启一个全双工通信会话,可以在不受同源策略限制的情况下进行数据交换。
  9. Service Workers

    • 通过Service Workers可以拦截网络请求并进行处理,从而实现跨域请求。

不伤代码是不是看着很呆,下面是以上方案做出的代码示例:

  1. 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) + ');'
    
  2. 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
    
  3. 代理服务器:
    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');
    });
    
  4. 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>
    
  5. 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');
        }
    });
    
  6. WebAssembly:
    由于WebAssembly的复杂性,这里不提供具体代码示例,但基本原理是使用fetchXMLHttpRequest从其他源加载WASM模块,然后使用WebAssembly的API来编译和实例化模块。

  7. 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);
    
  8. 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);
        });
    });
    
  9. Service Workers:

    self.addEventListener('fetch', function(event) {
        event.respondWith(
            fetch(event.request).catch(function() {
                return fetch('http://api.example.com/fallback');
            })
        );
    });
    
  • 26
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值