跨域解决方案详解:让前端开发者轻松应对跨域问题

382 阅读4分钟

1、浏览器的同源策略 ?

1.1 什么是同源策略

同源策略(Same-origin policy)是Web浏览器的一种安全功能,它限制了从一个源加载的文档或脚本如何与另一个源的资源进行交互。这个策略可以防止恶意网站读取另一个网站的数据,从而保护用户的信息安全。

1.2 什么是源

image.png

四个例子

例子1例子2是否同源原因
www.example.comwww.example.com不同源协议不同
www.example.comwww.example1.com不同源域名不同
www.example.comwww.example.com:8080不同源端口不同
www.example.comwww.example.com同源协议相同、域名相同、端口相同(端口默认80)

不同源,就是我们平常说的跨域

2、跨域的影响有哪些 ?

让我们在本地新建两个文件,index.htmltarget.html

2.1 限制DOM访问

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <iframe src="http://www.baidu.com" id="baidu"></iframe>
    <iframe src="./target.html" id="index"></iframe>
</body>
</html> 

image.png 可以看到本地的页面是可以拿到html的,百度的页面提示跨域了

2.2 限制Cookie访问

让我们在本地target.html设置下cookie
document.cookie = 'name=栋梁之才'

image.png 可以看到本地文件是可以的,百度的就提示跨域了

2.3 限制Ajax获取数据

 async function getNews(){
    // let url='https://tenapi.cn/v2/baiduhot';
    let url='https://www.baidu.com';
    let result= await fetch(url);
    let data=await result.json();
    console.log(data);
}

当我们这样利用ajax请求百度时,就会产生跨域问题

image.png CORS策略阻止了对“https:/”从源“http://”获取的访问:请求的资源上不存在“Access-Control-Allow-Origin”标头。如果不透明响应满足您的需求,请将请求的模式设置为“no-cors”以获取禁用CORS的资源。

总结:不同源的请求是无法直接访问的,这是浏览器限制的。
但是要理解一点:请求发出了,服务器也响应了,数据回来了,只是数据被浏览器劫持了,它不给你而已。
补充一点:link、script、image标签没有跨域限制
我们所说的跨域一般指ajax请求产生的跨域

3、解决跨域

3.1、CORS解决ajax跨域

CORS(Cross-Origin Resource Sharing,跨源资源共享)是一种机制,它允许限制资源(例如网页上的JavaScript脚本)在一个源(域)上被另一个源(域)的网页所请求。CORS是为了安全而设计的,它通过HTTP头部来允许或拒绝跨域请求。

让我们利用node起一个服务

npm init
npm i express
新建service.js

const express=require('express');
const app = express();

app.get('/', (req, res) => {
    res.json({ message: '你好!栋梁之才'})
});

const PORT = 3000;
app.listen(PORT, () => {
    console.log(`Server running on port ${PORT}`);
});

nodemon service.js启动服务

前端请求

 async function getNews(){
    let url='http://127.0.0.1:3000';
    let result= await fetch(url);
    let data=await result.json();
    console.log(data);
}

这样请求肯定会出现跨域问题,解决办法就是在相应头中设置cors,告诉浏览器我允许它获取数据

后端

//service.js
const express=require('express');
const app = express();

app.get('/', (req, res) => {
    res.setHeader('Access-Control-Allow-Origin', 'http://127.0.0.1:5500');
    //res.setHeader('Access-Control-Allow-Origin', '*');
    res.json({ message: '你好!我是栋梁之才11' });
});

const PORT = 3000;
app.listen(PORT, () => {
    console.log(`Server running on port ${PORT}`);
});

再次访问就可以正常拿到数据了,设置为*表示允许所有域名

image.png 没设置cors和设置cors响应头的区别

image.png 允许所有源这里就是*号

cors会把请求分为两类:简单请求和复杂请求,上边所说的方法是简单请求,简单请求要满足以下条件。除了简单请求,剩下的都是复杂请求,复杂请求需要先发送options类型的预检请求,这是浏览器自动发出的。

1、请求方法method只能是get、post、head其中的一种。
2、不自定义请求头。
3、Content-Type类型只能是:application/x-www-form-urlencodedmultipart/form-datatext/plain其中的一种。
让我们改造前端代码,让其变成复杂请求

 async function getNews(){
    let url='http://127.0.0.1:3000';
    let result= await fetch(url,{
        headers:{
            'Content-Type': 'application/json', // 非简单请求类型
        }
    });
    let data=await result.json();
    console.log(data);
}

image.png 浏览器提示需要预检,让我们来改造下后端代码

const express=require('express');
const app = express();
app.options('/', (req, res) => {
    res.setHeader('Access-Control-Allow-Origin', 'http://127.0.0.1:5500');
    res.setHeader('Access-Control-Allow-Methods', 'GET');
    res.setHeader('Access-Control-Allow-Headers', 'contentType');

    res.send()
});
app.get('/', (req, res) => {
    res.setHeader('Access-Control-Allow-Origin', 'http://127.0.0.1:5500');
    // res.setHeader('Access-Control-Allow-Origin', '*');
    res.json({ message: '你好!我是栋梁之才11' }); 
});

const PORT = 3000;
app.listen(PORT, () => {
    console.log(`Server running on port ${PORT}`);
});

浏览器调试可以看到,又可以正常请求接口了。建议使用第三方库统一处理相应,例如cors。cors解决跨域是基于后端是自己人的前提下。

3.2、JSONP解决跨域

JSONP(JSON with Padding)是一种利用<script>标签没有跨域限制的特性来发送跨域请求的技术。其基本思想是,在客户端定义一个全局函数,然后将这个函数名作为请求参数传递给服务器,服务器返回一个执行该函数的JavaScript代码,从而实现跨域数据传输。

其实就是利用script发送请求后可以执行js的的特性,后端发送一段js代码,前端执行。

前端代码

function jsonp(url, callback) {
    var callbackName = 'jsonp_callback_' + Math.random().toString(36).substring(7);
    window[callbackName] = function (data) {
        callback(data);
        document.body.removeChild(script);
        delete window[callbackName];
    };
    // 创建script标签,并设置src属性
    var script = document.createElement('script');
    script.src = url + '?callback=' + callbackName;
    document.body.appendChild(script);
}
jsonp('http://127.0.0.1:3000/app', data => console.log(data))

改造下后端代码

app.get('/app', (req, res) => {
    const callbackName = req.query.callback;
    res.send(callbackName+'({message: "你好!我是栋梁之才11"})');
});

image.png 这就是大名鼎鼎的jsonp了,现在很少用了,这个技术是早期浏览器不支持cors时被广泛使用的技术

3.3、配置代理解决跨域

未完待续