一、同源策略
什么是同源策略?
同源策略(Same-origin Policy):为了保证浏览器的信息安全,浏览器采用同源策略,保证当前源中的资源只能在当前的源中使用;其他源如果需要使用当前源资源,需要特殊技术,这种A源访问B源的资源的通信称为跨域;
示例:
let xhr = new XMLHttpRequest();
xhr.open('GET', 'https://www.baidu.com/', true);
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && xhr.status === 200) {
console.log('xxx')
}
};
xhr.send();
- 以上请求会报错:
Access to XMLHttpRequest at 'https://www.baidu.com/' from origin 'http://localhost:63342' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
当出现以上错误时说明你正在进行一个跨域的操作;
同源策略的要求:
同源策略要求通信的两个源的协议、域名、端口号要相同,如果三者中任意一个不同就是不满足同源策略;不满足同源策略的通信就是跨域;
常用的跨域解决方案:
- JSONP:利用 script 的 src 属性是不受同源策略约束的,可以访问不同服务器或者端口号下的数据
- 服务端转发,因为同源策略只在客户端存在,在服务端是不存在的;所以可以由服务端转发请求;webpack-dev-server的proxy
- nginx 转发,nginx 是服务器应用程序,它可以接受客户端的请求,然后根据规则可以配置自动转发;
- CORS: Cross-Origin-Resource-Sharing: 需要目标域设置 Access-Control-Allow-Origin 头信息;
- postMessage: 发送window.postMessage(); 接收window.onMessage()
- webSocket协议跨域:
- socket.on(‘connect’)
- socket.on(‘message’)
- socket.on(‘disconnect’)
- socket.send()
- document.domain + iframe
- window.name + iframe
- location.hash + ifram
二、JSONP
JSONP 是一种常用的解决跨域的方式;
原理
利用 script 的 src 属性是不受同源策略约束的,可以访问不同服务器或者端口号下的数据
- 提前声明一个叫做 fn 的函数,给 fn 设置一个形参;
- 在页面给 script 的 src 的指向的路径拼接一个 callback 属性,callback=fn;当浏览器解析到这个 script 标签时,会向 src 指向的路径发起 http 请求;
- 服务器收到这个请求后,会返回一个 fn (这里面是服务器返回的数据)
- fn({xxx}) 这个是让 fn 执行,小括号里面就是服务器发送给我们的数据
示例
JS代码:
function fn(data) {
console.log(data);
}
HTML代码
<script src="http://matchweb.sports.qq.com/kbs/calendar?columnId=100000&callback=fn"></script>
通过调用淘宝搜索接口的案例
<input type="text" id="search">
<input type="button" value="搜索" id="button">
<div id="app">test jsonp</div>
<script type="text/javascript">
const search = document.getElementById("search");
const button = document.getElementById("button");
const app = document.getElementById("app");
let searchSomething = ""
button.addEventListener("click", function (e) {
searchSomething = search.value
seachFn();
})
//添加<script>标签的方法
function addScriptTag(src) {
var script = document.createElement('script');
script.setAttribute("type", "text/javascript");
script.src = src;
document.body.appendChild(script);
}
function seachFn() {
//搜索taobao,将自定义的回调函数名 cb 传入callback参数中
addScriptTag(`https://suggest.taobao.com/sug?code=utf-8&q=${searchSomething}&callback=cb`);
}
//自定义的回调函数 cb
function cb(data) {
//我们就简单的获取 taobao 搜索结果的第一条记录中url数据
console.log(data);
app.innerText = JSON.stringify(data.result);
}
</script>
使用node搭建http服务实现jsonp
创建node服务
const http = require('http');
//引入url-parse模块解析url字符串
const urlParse = require('url-parse');
// 引入query-string模块解析url.query字符串
const queryString = require('query-string');
//创建新的HTTP服务器
const server = http.createServer();
//通过request事件来响应request请求
server.on('request',function(req, res){
const url = urlParse(req.url)
var urlPath = queryString.parse(url.query);
//如果urlPath为'jsonp',就认定该请求为携带jsonp方法的http请求
if(url.pathname === '/jsonp'){
res.writeHead(200,{'Content-Type':'application/json;charset=utf-8'});
var data = {
"name": "jsonp data"
};
data = JSON.stringify(data);
//前端的回调函数名为urlPath.callback
var callback = urlPath.callback+'('+data+');'; // 定义的callback供前端使用
res.end(callback);
}
else{
res.writeHead(200, {'Content-Type':'text/html;charset=utf-8'});
res.end('Hell World\n');
}
});
//监听8080端口
server.listen('8123',() => {
//用于提示我们服务器启动成功
console.log('Server running port 8123!');
});
运行 node server.js,启动http://localhost:8123服务
新建前端html页面
<body>
<script type="text/javascript">
//添加<script>标签的方法
function addScriptTag(src) {
var script = document.createElement('script');
script.setAttribute("type", "text/javascript");
script.src = src;
document.body.appendChild(script);
}
window.onload = function() {
//搜索taobao,将自定义的回调函数名 cb 传入callback参数中
addScriptTag(`http://localhost:8123/jsonp?callback=hello`);
}
function hello(data) {
console.log(data);
}
</script>
</body>