JS基础——之前端跨域请求解决方案
跨域问题解决
浏览器遵循的同源策略主要是指协议、域名、端口一致
只要任意一个不一样,就不是同源,触发了同源策略的请求我们叫跨域请求
基于http协议的跨域解决办法:
一、jsonp解决跨域请求
原理:script标签可以执行js代码,会把src指定的文件或地址里面的内容当作js代码执行,src属性不受同源策略的影响,这样我们可以在src属性中放置一个非同源的服务器,服务器可以返回一个字符串,前端拿到这个字符串当作js代码执行。
过程:
1.我们可以在src属性中放置一个非同源的服务器地址,在地址后面携带一个带有函数名的参数
2.服务器可以读取在这个函数名后面的参数,通过函数名(数据)这种字符串形式进行返回。
3.前端拿到这个字符串当作js代码执行。
缺点:
只能实现get一种请求。
1)原生js代码实现:
<script>
var script = document.createElement('script');
script.type = 'text/javascript';
// 传参一个回调函数名给后端,方便后端返回时执行这个在前端定义的回调函数
script.src = 'http://www.domain.com:8080/login?user=admain&callback=handleCallback';
document.head.appendChild(script);
// 回调执行函数
function handleCallback(res) {
alert(JSON.stringtify(res));
}
</script>
2)jquery ajax实现:
$.ajax({
url: 'http://www.domain.com:8080/login',
type: 'get',
dataType: 'jsonp', // 请求方式为jsonp
jsonpCallback: 'handleCallback', // 自定义回调函数名
data: {}
})
- vue.js实现
this.$http.jsonp('http://www.domain.com:8080/login', {
params: {},
jsonp: 'handleCallback'
}).then((res)=> {
console.log(res);
})
二、代理解决跨域请求(nodejs中间件代理跨域)
代理——利用正向代理实现
apache代理http协议免费 代理https协议收费
nginx代理http和https协议均免费
1. vue框架的跨域(1次跨域)
利用node+webpack+webpack-dev-server代理接口跨域。在开发环境下,由于vue渲染服务器和接口代理服务都是webpack-dev-server同一个,所以页面与代理接口之间不再跨域,无须设置headers跨域信息了
webpack.config.js部分配置:
module.exports = {
entry: {},
module: {},
...
devServer: {
historyApiFallback: true,
proxy: [{
context: '/login',
target: 'http://www.domain.come:8080', // 代理跨域目标接口
changeOrigin: true,
secure: false, // 当代理某些https服务报错时用
cookieDomainReweite: 'www.domain.com' // 可以为false,表示不修改
}],
noInfo: true
}
}
三、CORS 跨域资源共享
普通跨域请求:只服务端设置Access-Control-Allow-Origin即可,前端无须设置,若要带cookie请求:前后端都需要设置。
需注意的是:由于同源策略的限制,所读取的cookie为跨域请求借口所在的域cookie,而非当前页。
目前,所有浏览器都支持该功能(IE8+:IE8/9需要使用XDomainRequest对象来支持CORS),CORS也已经成为主流的跨域解决方案。
跨域请求并不是请求发不出去,请求实际上发出去了 服务器也给客户端响应了,浏览器不允许我们使用服务器的数据
此时可以让服务器通知浏览器,这个域名可以请求我的数据
1. 前端设置:
- 原生 ajax
// 前端设置是否带cookie
xhr.withCredentials = true;
示例代码:
var xhr = new XMLHttpRequest(); // IE8/9需要用window.XDomainRequest兼容
// 前端是否设置带cookie
xhr.withCredentials = true;
xhr.open('post','http://www.domain.com:8080/login',true);
xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
xhr.send('user=admin');
xhr.onReadystatechange = function() {
if(xhr.readyState == 4 && xhr.status == 200) {
alert(xhr.responseText);
}
}
2) jQuery ajax
$.ajax({
...
xhrFields:{
withCredentials: true // 前端设置是否带cookie
},
crossDomain: true, // 会让请求头中包含跨域的额外信息,但不会含cookie
...
})
- VUE框架
a) axios设置
axios.defaults.withCredentails = true
b) vue-resource设置
Vue.http.options.credentials = true
2. 服务端设置:
若后端设置成功,前端浏览器控制台则不会出现跨域报错信息,反之,则说明没设成功。
- php后端代码
<?php
header('Access-Control-Allow-Origin: *');
header('Access-Control-Request-Methods:GET,POST');
header('Access-Control-Allow-Headers:x-requested-with,content-type,test-token,test-sessid');
?>
2)java后端代码
/*
* 导入包:import jacax.servlet.http.HttpServletResponse;
* 接口参数中定义:HttpServletResponse response
*/
// 允许跨域访问的域名:若有端口需写全(协议+域名+端口),若没有端口末尾不用加'/'
response.setHeader("Access-Control-Allow-Origin","http://www.domain.com");
// 允许前端带认证cookie:启用此项后,上面的域名不能为'*',必须指定具体的域名,否则浏览器会提示
response.setHeader("Access-Control-Allow-Credentials","true");
// 提示OPTIONS预检时,后端需要设置的两个常用自定义头
response.setHeader("Access-Control-Allow-Headers","Content-Type,X-Requested-With");
- Nodejs后台示例
var http = require('http');
var server = http.createServer();
var qs = require('querystring');
server.on('request', function(req, res) {
var postData = '';
// 数据块接收中
req.addListener('data', function(chunk) {
postData += chunk;
});
// 数据接收完毕
req.addListener('end', function() {
postData = qs.parse(postData);
// 跨域后台设置
res.writeHead(200, {
'Access-Control-Allow-Credentials': 'true', // 后端允许发送Cookie
'Access-Control-Allow-Origin': 'http://www.domain1.com', // 允许访问的域(协议+域名+端口)
/*
* 此处设置的cookie还是domain2的而非domain1,因为后端也不能跨域写cookie(nginx反向代理可以实现),
* 但只要domain2中写入一次cookie认证,后面的跨域接口都能从domain2中获取cookie,从而实现所有的接口都能跨域访问
*/
'Set-Cookie': 'l=a123456;Path=/;Domain=www.domain2.com;HttpOnly' // HttpOnly的作用是让js无法读取cookie
});
res.write(JSON.stringify(postData));
res.end();
});
});
server.listen('8080');
console.log('Server is running at port 8080...');
四、 nginx代理跨域
1、 nginx配置解决iconfont跨域
浏览器跨域访问js、css、img等常规静态资源被同源策略许可,但iconfont字体文件(eot|otf|ttf|woff|svg)例外,此时可在nginx的静态资源服务器中加入以下配置。
location / {
add_header Access-Control-Allow-Origin *;
}
2、 nginx反向代理接口跨域
跨域原理: 同源策略是浏览器的安全策略,不是HTTP协议的一部分。服务器端调用HTTP接口只是使用HTTP协议,不会执行JS脚本,不需要同源策略,也就不存在跨越问题。
实现思路:通过nginx配置一个代理服务器(域名与domain1相同,端口不同)做跳板机,反向代理访问domain2接口,并且可以顺便修改cookie中domain信息,方便当前域cookie写入,实现跨域登录。
nginx具体配置:
#proxy服务器
server {
listen 81;
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;
}
}
1.) 前端代码示例:
var xhr = new XMLHttpRequest();
// 前端开关:浏览器是否读写cookie
xhr.withCredentials = true;
// 访问nginx中的代理服务器
xhr.open('get', 'http://www.domain1.com:81/?user=admin', true);
xhr.send();
2.) Nodejs后台示例:
var http = require('http');
var server = http.createServer();
var qs = require('querystring');
server.on('request', function(req, res) {
var params = qs.parse(req.url.substring(2));
// 向前台写cookie
res.writeHead(200, {
'Set-Cookie': 'l=a123456;Path=/;Domain=www.domain2.com;HttpOnly' // HttpOnly:脚本无法读取
});
res.write(JSON.stringify(params));
res.end();
});
server.listen('8080');
console.log('Server is running at port 8080...');