同源策略
所谓同源是指”协议+域名+端口”三者相同,即便两个不同的域名指向同一个IP地址,也非同源。
同源策略限制以下几种行为:
- Cookie、LoaclStorage 和 IndexDB 无法读取
- DOM 和 Js对象无法获得
- Ajax 请求不能发送
跨域解决方案
JSONP
通常为了减轻web服务器的负载,我们把js、css、img等静态资源分离到另一台独立域名的服务器上,在html页面中再通过相应的标签从不同域名下加载静态资源,而被浏览器允许,基于此原理,我们可以通过动态创建script,再请求一个带参网址实现跨域通信。 ```javascript const script = document.createElement(‘script’); script.type = ‘text/javascript’;
// 传参一个回调函数名给后端,后端返回时将参数传入并执行这个在前端定义的回调函数
<a name="KSzHH"></a>
#### 跨域资源共享(CORS)
普通跨域请求:服务端只需设置Access-Control-Allow-Origin即可,前端无需设置,若要带Cookie请求:前后端都需要设置。
需注意的是:由于同源策略的限制,所读取的cookie为跨域请求接口所在域的cookie,而非当前页。
<a name="4yLvX"></a>
##### 前端设置
原生Ajax
```javascript
var xhr = new XMLHttpRequest(); // IE8/9需用window.XDomainRequest兼容
// 前端设置是否带cookie
xhr.withCredentials = true;
xhr.open('post', 'http://www.domain2.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);
}
};
axios
axios.defaults.withCredentials = true;
node后端示例:
const http = require('http');
const server = http.createServer();
const qs = require('qs');
server.on('request',function(req,res){
let postData = '';
// 数据块接收中
req.addListener('data',function(chunk){
postData += chunk;
})
// 数据接收完毕
req.addListen('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'
})
res.write(JSON.stringify(postData));
res.end();
})
})
server.listen(3000)
nginx代理跨域
nginx配置解决iconfont跨域
浏览器跨域访问 js, css, img等常规静态资源被同源策略许可,但iconfont字体文件(eot|otf|ttf|woff|svg)例外,此时可在nginx的静态服务器中加入以下配置:
location / {
add_header Access-Control-Allow-Origin *;
}
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等中间件代理接口访问nginx时,此时无浏览器参与,故没有同源限制,下面的跨域配置可不启用
add_header Access-Control-Allow-Origin http://www.domain1.com; #当前端只跨域不为cookie时,可为*
add-header Access-Control-Allow-Credentials true;
}
}
前端代码示例:
const xhr = new XMLHttpRequest();
// 前端开关:浏览器是否读写Cookie
xhr.withCredentials = true;
// 访问nginx中的代理服务器
xhr.open('get','http://www.domain1.com:81/?user=domain',true);
xhr.send();
node后端示例:
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...');