跨域及其几种解决方案跨域指的是在浏览器中执行的脚本试图访问一个与脚本所在的域名、协议或端口不同的资源时所遇到的问题。这个
跨域指的是在浏览器中执行的脚本试图访问一个与脚本所在的域名、协议或端口不同的资源时所遇到的问题。这个问题通常由浏览器的同源策略引发。
那什么是同源策略呢?
1. 同源
1.1同源策略
同源策略(Same-Origin Policy, SOP)是浏览器的一种安全机制,用来防止恶意脚本从一个域访问另一个域的敏感数据。简而言之就是浏览器为确保数据安全而遵循的一种策略。
源=协议+域名+端口号
所以根据同源策略,只有当以下三者都相同时,两个页面才被认为是同源的:
- 协议(例如:
http://
vshttps://
) - 域名(例如:
example.com
vssub.example.com
) - 端口(例如:
http://www.a.com:80
vshttp://www.a.com:8080
)
如果一个网页从不同源(协议、域名、端口任意一个不同)的服务器请求数据,浏览器通常会拒绝此请求以防止潜在的安全问题,这就是所谓的“跨域”问题。
1.2 同源请求
假设我们的所处源是www.a.com/login ,目标源是www.a.com/api/login
所处源要向目标源发送一个请求。
由于这两个源的协议,域名,端口号都相同,所以他是同源的,这种请求就叫做同源请求
那如果目标源是abc.d.com/login ,这个源的域名与所处源域名不一致,就称为非同源请求
小结
所处源与目标源不一致,就是非同源,又称异源或跨域
2. 跨域问题示例
浏览器会对跨域做出一些限制
比如,源1和源2,这两个源是非同源的,浏览器就会有下面这些限制:
1. DOM访问限制
源1的脚本不能读取和操作源2的DOM
同源情况下:
//demo.html
<h2>www</h2>
<button onclick="showDOM()">获取页面2的DOM</button>
<br /><br />
<iframe id="framePage" src="./demo2.html"></iframe>
<script type="text/javascript">
function showDOM() {
const framePage = document.getElementById('framePage')
console.log(framePage.contentWindow.document.body);
}
</script>
//demo2.html
<h2>abc</h2>
(此时的两个页面是同源的)
非同源:
//demo.html
<h2>www</h2>
<button onclick="showDOM()">获取页面2的DOM</button>
<br /><br />
<iframe id="framePage" src="https://www.bilibili.com/" width="900px" height="500px"></iframe>
<script type="text/javascript">
function showDOM() {
const framePage = document.getElementById('framePage')
console.log(framePage.contentWindow.document.body);
}
</script>
无法访问document
2. cookie访问限制
源1不能访问源2的cookie
这里打印的是当前页面的cookie:
3.Ajax响应数据限制
源1可以给源2发请求,但是无法获取源2响应的数据
//demo3.html
<button onclick="getNews()">获取头条新闻</button>
<script type="text/javascript">
async function getNews() {
const url =
"http://www.toutiao.com/hot-event/hot-board/?origin=toutiao_pc";
let result = await fetch(url);
let data = await result.json();
console.log(data);
}
</script>
3. 常见的跨域解决方案
为了在安全的前提下实现跨域资源访问,开发者可以采用以下几种解决方案:
当然可以!以下是每种跨域解决方案的具体说明以及对应的代码示例:
1. JSONP (JSON with Padding)
适用场景: 只能用于GET请求。
工作原理: 通过动态生成一个<script>
标签请求跨域资源,并在响应中执行一个回调函数。
代码示例:
前端代码:
<!DOCTYPE html>
<html>
<head>
<title>JSONP Example</title>
</head>
<body>
<script>
function handleResponse(data) {
console.log('Received data:', data);
}
// 动态生成 script 标签
const script = document.createElement('script');
script.src = 'https://example.com/api/data?callback=handleResponse';
document.body.appendChild(script);
</script>
</body>
</html>
后端代码(假设使用Node.js):
app.get('/api/data', (req, res) => {
const callback = req.query.callback;
const data = { message: 'Hello, JSONP!' };
res.send(`${callback}(${JSON.stringify(data)})`);
});
说明: 当浏览器加载这个<script>
时,它会执行handleResponse
函数并传入跨域数据。
2. CORS (Cross-Origin Resource Sharing)
适用场景: 适用于所有HTTP方法,如GET、POST、PUT、DELETE等。
工作原理: 服务器在响应头中设置允许跨域的源头,并通过浏览器实现安全的跨域请求。
代码示例:
前端代码:
fetch('https://api.example.com/data', {
method: 'GET',
headers: {
'Content-Type': 'application/json'
}
})
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Error:', error));
后端代码(假设使用Express):
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', '*'); // 或者指定特定域名如 'https://mywebsite.com'
res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
res.header('Access-Control-Allow-Headers', 'Content-Type');
next();
});
app.get('/data', (req, res) => {
res.json({ message: 'Hello, CORS!' });
});
说明: 服务器通过设置Access-Control-Allow-Origin
等头信息来允许跨域访问,客户端可以通过标准的HTTP请求访问跨域资源。
3. 服务器代理
适用场景: 适合所有类型的跨域请求。
工作原理: 前端请求同源服务器,同源服务器充当代理,转发请求至目标服务器,再将结果返回前端。
代码示例:
前端代码:
fetch('/proxy/api/data', {
method: 'GET',
headers: {
'Content-Type': 'application/json'
}
})
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Error:', error));
服务器代理代码(假设使用Express):
const { createProxyMiddleware } = require('http-proxy-middleware');
app.use('/proxy', createProxyMiddleware({
target: 'https://api.example.com',
changeOrigin: true,
pathRewrite: {
'^/proxy': '', // 将 /proxy 去除,转发到实际的 API 地址
},
}));
app.get('/api/data', (req, res) => {
res.json({ message: 'Hello from Proxy!' });
});
说明: 前端请求本地服务器的/proxy/api/data
,服务器将其代理到https://api.example.com/data
并返回响应,从而实现跨域访问。
4. iframe + postMessage
适用场景: 需要在不同域之间进行通信的场景。
工作原理: 通过iframe
嵌入不同域的页面,并使用postMessage
在父子窗口间传递数据。
代码示例:
父页面代码(假设域名为https://parent.com
):
<!DOCTYPE html>
<html>
<head>
<title>Parent Page</title>
</head>
<body>
<iframe id="childFrame" src="https://child.com/child.html" style="width:600px; height:400px;"></iframe>
<script>
const childFrame = document.getElementById('childFrame');
// 向子页面发送消息
childFrame.onload = function() {
childFrame.contentWindow.postMessage('Hello from parent!', 'https://child.com');
};
// 接收子页面的消息
window.addEventListener('message', function(event) {
if (event.origin === 'https://child.com') {
console.log('Received from child:', event.data);
}
});
</script>
</body>
</html>
子页面代码(假设域名为https://child.com
):
<!DOCTYPE html>
<html>
<head>
<title>Child Page</title>
</head>
<body>
<script>
// 接收父页面的消息
window.addEventListener('message', function(event) {
if (event.origin === 'https://parent.com') {
console.log('Received from parent:', event.data);
// 向父页面发送消息
event.source.postMessage('Hello from child!', event.origin);
}
});
</script>
</body>
</html>
说明: 通过postMessage
方法,父页面和子页面可以进行安全的跨域通信。
以上就是文本全部内容,希望对你有所帮助,感谢你的阅读!
转载自:https://juejin.cn/post/7408037561237209099