同源策略
- 协议
- 域名
- 端口
三个元素某个不同都属于跨域
常见的解决方法
1.跨域资源共享(CORS)
1.1 安全请求
(1)方法:GET、POST或HEAD
(2)请求头(只包含以下请求头):
Accept、Accept-Language、Content-Language、Content-Type的值为application/x-www-form-urlencoded、multipart/form-data或text/plain
1.2 其他请求
1.2.1 预检请求
- 浏览器自主发出,请求服务器访问资源许可
- options请求
- 没有请求体
- 请求头:
Access-Control-Request-Method
Access-Control-Request-Headers
服务器同意客户端请求,则响应头会携带以下:
- Access-Control-Allow-Origin 请求源为* 则是任意源头都可
- Access-Control-Allow-Methods 允许的方法
- Access-Control-Allow-Headers 允许的请求头
- Access-Control-Max-Age 缓存此权限的秒数,这个时间范围内后续请求将不会触发预检
1.2.2 实际请求
预检请求通过后,实际请求响应头还是会加上Access-Control-Allow-Origin,然后客户端就可以获取到服务端资源了
需要注意:
如果需要携带凭据(cookie或者其他)的话:
客户端请求需要携带credentials:"include":
fetch("https://xxx.com", {
credentials: "include",
});
服务器响应头会返回Access-Control-Allow-Credentials:true :
2. JSONP
动态创建script标签,并注入get请求实现跨域请求~
3.使用代理服务器
1.前后端分离项目在开发阶段会在本地配置代理:
以下是常见打包工具配置:
(1)webpack
module.exports = {
entry: '',
output: {
filename: '',
path: path.resolve(__dirname, 'dist')
},
devServer: {
...
port: 9090,
proxy: {
'/api': {
target: 'http://localhost:3000',
changeOrigin: true,
pathRewrite: { '^/api': '' }
}
}
}
};
(2)vite
export default defineConfig({
server: {
proxy: {
'/api': {
target: 'http://localhost:3000',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, ''),
},
},
},
});
2. 生产会使用nginx或者其他web服务器去配置代理
server {
listen 80;
server_name xxx;
location / api / {
proxy_pass http://localhost:3000/;
proxy_set_header Host $host;
proxy_set_header X- Real - IP $remote_addr;
proxy_set_header X - Forwarded - For $proxy_add_x_forwarded_for;
proxy_set_header X - Forwarded - Proto $scheme;
# CORS Headers
add_header Access - Control - Allow - Origin *;
add_header Access - Control - Allow - Methods 'GET, POST, OPTIONS';
add_header Access - Control - Allow - Headers 'DNT,User-Agent,X-Requested-
With,If-Modified-Since,Cache-Control,Content-Type,Range';
add_header Access - Control - Expose - Headers 'Content-Length,Content-Range';
if ($request_method = 'OPTIONS') {
return 204;
}
}
location / {
root /var/www/html;
index index.html;
}
}
4.iFrame和PostMessage (一般不会用,除非特殊场景)
主页面:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>example</title>
</head>
<body>
<h1>example</h1>
<div id="message"></div>
<iframe id="frame" src="http://xx/yy" style="display:none;"></iframe>
<script>
window.onload = function(){
const iframe = document.getElementById('frame');
window.addEventListener('message', (event) => {
if (event.origin !== 'http://xx') return;
const data = event.data;
document.getElementById('message').innerText = data.message;
});
function fetchData() {
iframe.contentWindow.postMessage('queryData', 'http://xx');
}
fetchData();
}
</script>
</body>
</html>
iframe 内部页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>iframe</title>
</head>
<body>
<script>
window.addEventListener('message', (event) => {
if (event.data === 'queryData') {
fetch('http://yy')
.then(response => response.json())
.then(data => {
event.source.postMessage(data, event.origin);
});
}
});
</script>
</body>
</html>