跨域
浏览器的同源策略: 请求资源时,浏览器要求当前网页和请求资源的server必须同源。
同源: 协议、域名、端口三者必须一致。
处理跨域的方法:
1. jsonp
jsonp的原理:
利用script标签不受跨域限制和服务器端配合动态拼接字符串返回来实现跨域。
浏览器脚本动态创建script标签,并且给script的src属性加入了callback参数,浏览器端还要定义callback,callback内是读取数据的逻辑。(借助script来发送跨域请求,请求结束后script会被自动删除。)
服务器端配合接收callback保存的函数名,然后将要返回的数据填充到用函数名动态拼接的函数调用语句里返回,客户端收到js语句执行。
script只能识别js语句,一旦接收到js语句就立即执行。
jsonp方法的缺点: 只支持get请求,并且需要服务端配合才可以。
2. CORS:服务器设置http-header
对于普通的跨域请求:只服务端设置Access-Control-Allow-Origin即可,前端无须设置,若要带cookie请求:前后端都需要设置。
前端设置是否带cookie
// 前端设置是否带cookie
xhr.withCredentials = true;
服务器端设置
response.setHeader("Access-Control-Allow-Origin", "http://www.domain1.com"); // 若有端口需写全(协议+域名+端口)
response.setHeader("Access-Control-Allow-Credentials", "true");允许跨域携带cookie
res.writeHead(200, {
'Access-Control-Allow-Credentials': 'true', // 允许跨域携带cookie
'Access-Control-Allow-Origin': 'http://www.demo1.com', // 允许访问的域(协议+域名+端口)
'Set-Cookie': 'l=a123456;Path=/;Domain=www.demo2.com;HttpOnly' // HttpOnly:脚本无法读取cookie
});
浏览器把请求分为简单请求和非简单请求。对于这两种请求,CORS的处理是不一样的.
满足以下两个条件的是简单请求:
- 请求方式为GET、POST、HEAD
- http头信息不超出一下字段:Accept、Accept-Language 、 Content-Language、 Last-Event-ID、 Content-Type(限于三个值:application/x-www-form-urlencoded、multipart/form-data、text/plain)
- 非简单请求:
如请求方法是PUT或DELETE,或者Content-Type字段的类型是application/json。
非简单请求的CORS请求,会在正式通信之前,增加一次HTTP查询请求,称为"预检"请求(preflight)。
这个 preflight 的作用在于,确认当前网页所在的域名是否在服务器的许可名单之中、明确可以使用的 HTTP 请求方法和头信息字段。只有在这个请求返回成功的情况下,浏览器才会发出正式的请求。"预检"请求用的请求方法是OPTIONS。
3. postMessage跨域
在HTML5中新增了postMessage方法,postMessage可以实现跨文档消息传输。
这个API通过调用发送信息的postMessage方法、注册监听信息的Message事件,实现跨窗口通信。
postMessage(data,origin)方法接受两个参数:
data: 传参时最好用JSON.stringify()序列化。
origin: 协议+主机+端口号,也可以设置为"*",表示可以传递给任意窗口,如果要指定和当前窗口同源的话设置为"/"。
举例说明:
两个页面之间进行数据传输,postMessage示例:
启动了两个ip地址来代表不同域名,页面t_hotnotes_list.html插入如下代码
<iframe id="iframe" src="http://10.73.154.73:8088/rbc/t/search_role.html" style="display:none;"></iframe>
<script>
var iframe = document.getElementById('iframe');
iframe.onload = function() {
var data = {
name: 'aym',
type:'wuhan'
};
// 向domain2传送跨域数据
iframe.contentWindow.postMessage(JSON.stringify(data), 'http://10.73.154.73:8088');
};
// 接受domain2返回数据,这边给延迟的原因,因为同步传输时,页面不一定立马拿到数据,所以给延迟
setTimeout(function(){
window.addEventListener('message', function(e) {
alert('data from domain2 sss ---> ' + e.data);
}, false);
},10)
</script>
页面search_role.html插入如下代码:
<script>
// 接收domain1的数据
window.addEventListener('message', function(e) {
console.log(e.data);
var data = JSON.parse(e.data);
if (data) {
data.number = 16;
data.age = 89;
data.icon = 'sfafdafdafasdf';
// 处理后再发回domain1
window.parent.postMessage(JSON.stringify(data), 'http://10.73.154.72:8088');
}
}, false);
</script>
4. Nginx反向代理接口跨域
正向代理隐藏了真实的客户端。反向代理隐藏了真实的服务器。Nginx 是一个很好的反向代理服务器。Nginx是一个 Web服务器,也可以用作反向代理,负载平衡器和 HTTP缓存。
Nginx解决跨域问题通过Nginx反向代理将对真实服务器的请求转移到本机服务器来避免浏览器的"同源策略限制"。
实现思路:当http://localhost:8080/abc请求http://10.10.10.10:1000/abc,出现跨域请求,通过nginx配置一个代理服务器做跳板机,反向代理访问http://10.10.10.10:1000/abc接口
nginx具体配置:
#proxy服务器
#proxy_pass 后面跟着一个 URL,用来将请求反向代理到 URL 参数指定的服务器上。
server {
listen 8080;
server_name localhost;
location / {
root html;
index index.html index.htm;
}
//一般只需要在该处添加代理配置信息
location /api/ {
rewrite ^.+/api/?(.*)$ /$1 break;
include uwsgi_params;
proxy_pass http://10.10.10.10:1000/;
}
}
前端代码示例:
$ajax({
type: "post",
datatype: "text",
// 添加`/api/`作为匹配项
url: "/api/abc",
// 如果ajax的application/json方式,data参数是字符串类型的,使用JSON.stringify()转换
data: JSON.stringify(params),
success: function(data){
//如果返回数据是json字符串,使用JSON.parse()转换
console.log(JSON.parse(data));
},
error: function(err){
console.log(err);
}
})
5. Nodejs中间件代理跨域
node中间件实现跨域代理,原理大致与nginx相同,都是通过启一个代理服务器,实现数据的转发,也可以通过设置cookieDomainRewrite参数修改响应头中cookie中域名,实现当前域的cookie写入,方便接口登录认证。
1)非vue框架的跨域
使用node + express + http-proxy-middleware搭建一个proxy服务器。
前端代码:
var xhr = new XMLHttpRequest();
// 前端开关:浏览器是否读写cookie
xhr.withCredentials = true;
// 访问http-proxy-middleware代理服务器
xhr.open('get', 'http://www.domain1.com:3000/login?user=admin', true);
xhr.send();
中间件服务器代码:
var express = require('express');
var proxy = require('http-proxy-middleware');
var app = express();
app.use('/', proxy({
// 代理跨域目标接口
target: 'http://www.domain2.com:8080',
changeOrigin: true,
// 修改响应头信息,实现跨域并允许带cookie
onProxyRes: function(proxyRes, req, res) {
res.header('Access-Control-Allow-Origin', 'http://www.domain1.com');
res.header('Access-Control-Allow-Credentials', 'true');
},
// 修改响应信息中的cookie域名
cookieDomainRewrite: 'www.domain1.com' // 可以为false,表示不修改
}));
app.listen(3000);
console.log('Proxy server is listen at port 3000...');
2)vue框架的跨域
node + vue + webpack + webpack-dev-server搭建的项目,跨域请求接口,直接修改vue.config.js配置。开发环境下,vue渲染服务和接口代理服务都是webpack-dev-server同一个,所以页面与代理接口之间不再跨域。
vue.config.js部分配置:
module.exports = {
entry: {},
module: {},
...
devServer: {
historyApiFallback: true,
proxy: [{
context: '/login',
target: 'http://www.domain2.com:8080', // 代理跨域目标接口
changeOrigin: true,
secure: false, // 当代理某些https服务报错时用
cookieDomainRewrite: 'www.domain1.com' // 可以为false,表示不修改
}],
noInfo: true
}
}