跨域

什么是跨域?

跨域是浏览器为了安全着想(防止XSS、CSFR攻击)而对网页的某些行为做出限制的一种策略。

当一个域请求或访问另一个域的资源的时候,就产生了跨域策略限制。

跨域策略限制有:

  • 不允许访问不同域的cookie和webStorage
  • 不允许发送Ajax请求
  • 不允许访问DOM、和js对象

怎么判断是不是两个不同的域?

当协议、域名、端口号中的其中一个不一样的时候就是两个不同的域

但是在真实业务开发的时候,我们往往都要进行跨域(发送ajax请求),于是,业界就涌现了很多的跨域方法。

  • JSONP跨域
  • CORS
  • 服务器代理请求
    • nginx反向代理请求
    • nodejs中间件代理请求
  • web Socket
  • window.name + iframe
  • document.domain + iframe
  • location.hash + iframe
  • postMessage

下面来了解他们的使用方法、实现原理

jsonp

实现原理:要发送请求,我们不一定非要使用ajax,我们还可以使用script元素,script元素会根据src属性下载js脚本,不受浏览器同源策略限制。

使用方法:

  • 准备好请求的url,往url里面添加参数,在url的末尾添加callback函数回调名

  • 动态创建一个script标签,将请求的url放到该script标签的src属性下,将该script标签添加到页面,也就意味这请求正式发出
  • 后台接收到请求,处理完请求后返回调用的回调函数,将返回的信息作为参数传递给回调参数
  • 前端在回调函数处理返回的信息

一段示例代码:

var url = 'www.baidu.com?q=uu&w=ss&callback=handlecallback';
var scri = document.createElement('script');
scri.src = url;
function handlecallback (data) {
    // data就是后台传递回来的数据
}
ducument.body.appendChild(scri); // 只有被添加到页面中才能真正发挥作用

特点:后台人员返回信息的时候需要返回callback函数的调用

缺点:只能实现get请求

CORS

CORS全称_Corss-Origin Resource Sharing_, 是现在比较流行的跨域解决方案之一。

实现原理:某些非简单请求触发浏览器发出预检请求,浏览器发出的预检请求主要是询问当前请求域是否被允许请求该服务器的资源(服务器是否允许本次请求),若服务器返回了肯定的回答,则浏览器正式允许本次请求的发出。

使用方法:前端使用方法跟正常的ajax请求无异。服务器端设置Access-Control-Allow-Origin为正确的域。

使用方法(跟正常的ajax请求主要是多了一个设置是否带上cookie):

var xhr = new XMLHttpRequest()
xhr.open('post', queryurl);
xhr.withCredentials = true;  // 设置为true,请求的时候一并发送cookie
xhr.send();

后端需要设置的resonseHeader:

{
    'Access-Control-Allow-Credentials': 'true',  // 是否允许前端的请求中带上cookie
    'Access-Control-Allow-Origin': 'domain/*',  // 这个属性的值可以是一个指定的domain或者表示任意domain的*
}

使用注意点:如果设置Access-Control-Allow-Origin的值为*(可以让所有的域访问),那么前端便不能正确的保存cookie,而且前端发出ajax请求的过程中也不能带上cookie,否则会被限制。

参考:HTTP访问控制

nginx代理跨域

真正限制跨域资源访问的是浏览器,而非HTTP这个协议,如果我们在服务器上发出一些跨域的HTTP请求,那么请求就不会受到限制,于是,就出现了nginx代理跨域

我们在源服务器之上加上一个nginx代理服务器,让用户通过访问代理服务器来访问真正的服务器资源,资源请求也全部发送到代理服务器,如果是跨域的资源请求,则url带上约定的特定标识,那么,我们在代理服务器上就可以发现那些特定的url,分别 请求特定的url(跨域资源)和常规的目标url(源服务器)。从而将跨域的资源访问从前端浏览器转移到服务器中。

实现该功能只需要配置好nginx服务器就好。

location /api/ {
    rewrite ^/api/(.*)/$1 break;   #api即为约定的特定的标识,方便nginx过滤请求,真正实现跨域资源访问的时候要移除这个前缀
    proxy_pass http://www.serverA.com;  #请求代理到这个目标网址中
    proxy_cookie_path: /ling/;  # 防cookie丢失 后台设置的cookie的路径,代理这个以便能真正的带上cookie和保存cookie
}

特点:强大,灵活

nodejs中间件代理跨域

与nginx代理跨域的原理类似,只是在实现上有所不同

WebSocket跨域

WebSocket是HTML5中新提出的一种在单个TCP连接上进行全双工通信的协议,与HTTP协议不同,也就不存在浏览器的跨域限制

更多WebSocket的介绍

window.name + iframe

实现原理:window.name的name值在不同的页面(跨域加载)加载后依旧存在,并且可以支持非常大的name值(2Mb)

使用方法:

  • 创建一个用户不可见的iframe
  • 将iframe的域设置为要请求资源的域,请求资源完成后,服务器将请求的数据写入window.name中
  • iframe将当前域设置回原来的域,并调用回调函数
  • 从回调函数中读取返回的信息

window.location.hash + iframe

document.domain + iframe

两个页面都通过js强制设置document.domain为基础主语,就实现了同域。限制:仅限主域相同,子域不同的跨域应用场景

POSTMessage + iframe