由于浏览器同源策略,凡是发送请求url的协议、域名、端口三者之间任意一与当前页面地址不同即为跨域。具体可以查看下表:
1、JSONP
1) JSONP原理
利用 标签没有跨域限制的漏洞,网页可以得到从其他来源动态产生的 JSON 数据。JSONP请求一定需要对方的服务器做支持才可以。
注意
:JSONP都是GET和异步请求的,不存在其他的请求方式和同步请求,且jQuery默认就会给JSONP的请求清除缓存。
2) JSONP和AJAX对比
JSONP和AJAX相同,都是客户端向服务器端发送请求,从服务器端获取数据的方式。但AJAX属于同源策略,JSONP属于非同源策略(跨域请求)
3) JSONP优缺点
JSONP优点是简单兼容性好,可用于解决主流浏览器的跨域数据访问的问题。缺点是仅支持get方法具有局限性,不安全可能会遭受XSS攻击。
4) 代码演示
function jsonp({ url, params, callback }) {
return new Promise((resolve, reject) => {
let script = document.createElement('script')
window[callback] = function(data) {
resolve(data)
document.body.removeChild(script)
}
params = { ...params, callback } // wd=b&callback=show
let arrs = []
for (let key in params) {
arrs.push(`${key}=${params[key]}`)
}
script.src = `${url}?${arrs.join('&')}`
document.body.appendChild(script)
})
}
jsonp({
url: 'http://localhost:3000/say',
params: { wd: 'Iloveyou' },
callback: 'show'
}).then(data => {
console.log(data)
})
2、CORS
1) CORS 原理
CORS 需要浏览器和后端同时支持。IE 8 和 9 需要通过 XDomainRequest 来实现。
浏览器会自动进行 CORS 通信,实现 CORS 通信的关键是后端。只要后端实现了 CORS,就实现了跨域。
服务端设置 Access-Control-Allow-Origin 就可以开启 CORS。 该属性表示哪些域名可以访问资源,如果设置通配符则表示所有网站都可以访问资源。
2) 代码演示
// index.html
let xhr = new XMLHttpRequest()
document.cookie = 'name=xiamen' // cookie不能跨域
xhr.withCredentials = true // 前端设置是否带cookie
xhr.open('PUT', 'http://localhost:4000/getData', true)
xhr.setRequestHeader('name', 'xiamen')
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
console.log(xhr.response)
//得到响应头,后台需设置Access-Control-Expose-Headers
console.log(xhr.getResponseHeader('name'))
}
}
}
xhr.send()
3、postMessage
1) postMessage 原理
postMessage是HTML5 XMLHttpRequest Level 2中的API,且是为数不多可以跨域操作的window属性之一,它可用于解决以下方面的问题:
页面和其打开的新窗口的数据传递
多窗口之间消息传递
页面与嵌套的iframe消息传递
上面三个场景的跨域数据传递
postMessage()方法允许来自不同源的脚本采用异步方式进行有限的通信,可以实现跨文本档、多窗口、跨域消息传递。
2) 代码演示
// a.html
//等它加载完触发一个事件
//内嵌在http://localhost:3000/a.html
4、websocket
1) Websocket 原理
Websocket是HTML5的一个持久化的协议,它实现了浏览器与服务器的全双工通信,同时也是跨域的一种解决方案。WebSocket和HTTP都是应用层协议,都基于 TCP 协议。但是 WebSocket 是一种双向通信协议,在建立连接之后,WebSocket 的 server 与 client 都能主动向对方发送或接收数据。同时,WebSocket 在建立连接时需要借助 HTTP 协议,连接建立好了之后 client 与 server 之间的双向通信就与 HTTP 无关了。
2) 代码演示
// socket.html
5、Node中间件代理
1) Node中间件代理原理
同源策略是浏览器需要遵循的标准,而如果是服务器向服务器请求就无需遵循同源策略。
2) 代码演示
// index.html(http://127.0.0.1:5500)
6、nginx反向代理
1) nginx反向代理原理
实现原理类似于Node中间件代理,需要你搭建一个中转nginx服务器,用于转发请求。
使用nginx反向代理实现跨域,是最简单的跨域方式。只需要修改nginx的配置即可解决跨域问题,支持所有浏览器,支持session,不需要修改任何代码,并且不会影响服务器性能。
2) 代码演示
// proxy服务器
server {
listen 80;
server_name www.domain1.com;
location / {
proxy_pass http://www.domain2.com:8080; #反向代理
proxy_cookie_domain www.domain2.com www.domain1.com; #修改cookie里域名
index index.html index.htm;
# 当用webpack-dev-server等中间件代理接口访问nignx时,此时无浏览器参与,故没有同源限制,下面的跨域配置可不启用
add_header Access-Control-Allow-Origin http://www.domain1.com; #当前端只跨域不带cookie时,可为*
add_header Access-Control-Allow-Credentials true;
}
}
7、window.name+iframe
1) window.name+iframe原理
window.name属性的独特之处:name值在不同的页面(甚至不同域名)加载后依旧存在,并且可以支持非常长的 name 值(2MB),通过iframe的src属性由外域转向本地域,跨域数据即由iframe的window.name从外域传递到本地域。这个就巧妙地绕过了浏览器的跨域访问限制,但同时它又是安全操作。
2) 代码演示
// a.html(http://localhost:3000/b.html)
8、location.hash + iframe
1) location.hash + iframe原理
a.html欲与c.html跨域相互通信,通过中间页b.html来实现。 三个页面,不同域之间利用iframe的location.hash传值,相同域之间直接js访问来通信。
2) 代码演示
// a.html
// b.html
// c.html
console.log(location.hash);
let iframe = document.createElement('iframe');
iframe.src = 'http://localhost:3000/b.html#idontloveyou';
document.body.appendChild(iframe);
9、document.domain + iframe
1) document.domain + iframe原理
两个页面都通过js强制设置document.domain为基础主域,就实现了同域。
2) 代码演示
// a.html
helloa
// b.html
hellob
10、xdr
1) xdr原理
这是IE8、IE9提供的一种跨域解决方案,功能较弱只支持get跟post请求,而且对于协议不同的跨域是无能为力的,比如在http协议下发送https请求。
2) 代码演示
XDomainRequest
11、proxy代理
1) proxy代理原理
这种方式首先将请求发送给后台服务器,通过服务器来发送请求,然后将请求的结果传递给前端。
注意:
对于同一请求浏览器通常会从缓存中读取数据,我们有时候不想从缓存中读取,所以会加一个preventCache参数,这个时候请求url变成:url?preventCache=12345567…;这本身没有什么问题,问题出在当使用某些前端框架(比如jquery)发送proxy代理请求时,请求url为proxy?url,同时设置preventCache:true,框架不能正确处理这个参数,结果发出去的请求变成proxy?url&preventCache=123456(正长应为proxy?url?preventCache=12356);后端截取后发送的请求为url&preventCache=123456,根本没有这个地址,所以你得不到正确结果。
2) 代码演示
React框架
const port = 8081
const url = 'http://localhost:' + port
const host = process.env.DEV_SERVER_HOST || 'localhost'
module.exports = {
init: function () {
return {
port,
hot: true,
publicPath: '/asseted/',
proxy: {
'/v2/**/*': {
target: `http://127.0.0.1:8080/`,
secure: false
}
},
historyApiFallback: {
rewrites: [
{
from: /./,
to: '/asseted/index.html'
},
]
},
// disableHostCheck: true
}
},
port,
host,
url
}
Vue框架
const express = require('express');
const proxy = require("http-proxy-middleware");
const app = express();
/*接口代理*/
app.use('/api/**',
proxy({
target: 'http://127.0.0.1:9180/',
changeOrigin: true,
pathRewrite: {"/api": ""} // 地址重写
})
);
/*加载静态资源*/
app.use(express.static('src'));
/*路由处理*/
app.get("/", (req, res) => {
res.sendfile("./index.html");
});
app.get("/chat", (req, res) => {
res.sendfile("./chat.html");
});
app.get("/table", (req, res) => {
res.sendfile("./table.html");
});
/*接口监听*/
app.listen(8000, () => {
console.log("项目启动与:http://localhost:8000");
});
参考: https://www.cnblogs.com/dojo-lzz/p/4265637.html
参考:http://web.jobbole.com/95654/