关于跨域的知识及常用方法
首先,问一下, 为什么会有跨域这个名词的出现呢? 跨域又是什么呢? 为何要跨域?浏览器的同源策略又是什么? 怎么解决? jsonp又是什么? 跨域的原理又是什么呢?
提到跨域,这里不得不说一下 “同源策略”, 何为同源?只有当协议、端口、和域名都相同的页面,则两个页面具有相同的源。只要网站的 协议名protocol、 主机host、 端口号port 这三个中的任意一个不同,网站间的数据请求与传输便构成了跨域调用,会受到同源策略的限制. 同源策略限制从一个源加载的文档或脚本如何与来自另一个源的资源进行交互。这是一个用于隔离潜在恶意文件的关键的安全机制。浏览器的同源策略,出于防范跨站脚本的攻击,禁止客户端脚本(如 JavaScript)对不同域的服务进行跨站调用(通常指使用XMLHttpRequest请求).
同源策略是浏览器做的一件好事,是用来防御来自邪门歪道的攻击,但总不能为了不让坏人进门而把全部人都拒之门外吧。没错,我们这种正人君子只要打开方式正确,就应该可以跨域.
那么,现在我们来简单聊一下,跨域到底跨域如何有效的解决?
- 通过 jsonp 跨域
貌似我们一遇到跨域,大家都会异口同声的说jsonp ,那么jsonp 具体是什么东西呢?
在js中,我们直接用XMLHttpRequest请求不同域上的数据时,是不可以的。但是,在页面上引入不同域上的js脚本文件却是可以的,jsonp正是利用这个特性来实现的.一些标签比如script、img这样的获取资源的标签是没有跨域限制的,所以jsonp 的原理是 动态创建script标签 , 才能实现 。
这里是后端简单的代码 ,返回path 为 jsonp 的路由,query 参数为 cb
query.cb是前后端约定的方法名字,其实就是后端返回一个直接执行的方法给前端, 由于前端是用script标签发起的请求, 所以返回了这个方法后相当于立马执行,并且把要返回的数据放在方法的参数里。
这里是前端页面,通过script标签 拼接了一个 callback,回调函数名称是 cb ,执行函数是 jsonpcb。这里简要说明一下,cb这个名称,不是你随写的,这个函数名,一般来说是你和后端开发人员协商好的,或者是后端开发人员定义的,你只要执行这个对于的回调函数就可以了.这里就是简单的jsonp的说明了,也是最重要的一部分。 - 空iframe + form
细心的朋友可能发现,JSONP只能发GET请求,因为本质上script加载资源就是GET,那么如果要发POST请求怎么办呢?
后端写个简单的接口
// 处理成功失败返回格式的工具
const {successBody} = require(’…/utli’)
class CrossDomain {
static async iframePost (ctx) {
let postData = ctx.request.body
console.log(postData)
ctx.body = successBody({postData: postData}, ‘success’)
}
}
module.exports = CrossDomain
前端封装一下请求
const requestPost = ({url, data}) => {
// 首先创建一个用来发送数据的iframe.
const iframe = document.createElement(‘iframe’)
iframe.name = ‘iframePost’
iframe.style.display = ‘none’
document.body.appendChild(iframe)
const form = document.createElement(‘form’)
const node = document.createElement(‘input’)
// 注册iframe的load事件处理程序,如果你需要在响应返回时执行一些操作的话.
iframe.addEventListener(‘load’, function () {
console.log(‘post success’)
})
form.action = url
// 在指定的iframe中执行form
form.target = iframe.name
form.method = ‘post’
for (let name in data) {
node.name = name
node.value = data[name].toString()
form.appendChild(node.cloneNode())
}
// 表单元素需要添加到主文档中.
form.style.display = ‘none’
document.body.appendChild(form)
form.submit()
// 表单提交后,就可以删除这个表单,不影响下次的数据发送.
document.body.removeChild(form)
}
// 使用方式
requestPost({
url: ‘http://localhost:9871/api/iframePost’,
data: {
msg: ‘helloIframePost’
}
})
3. CROS
Cross-Origin Resource Sharing(CORS)跨域资源共享是一份浏览器技术的规范,提供了 Web 服务从不同域传来沙盒脚本的方法,以避开浏览器的同源策略,确保安全的跨域数据传输。现代浏览器使用CORS在API容器如XMLHttpRequest来减少HTTP请求的风险来源。与 JSONP 不同,CORS 除了 GET 要求方法以外也支持其他的 HTTP 要求。服务器一般需要增加如下响应头的一种或几种:
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: X-PINGOTHER, Content-Type
Access-Control-Max-Age: 86400
跨域请求默认不会携带Cookie信息,如果需要携带,请配置下述参数:
“Access-Control-Allow-Credentials”: true
// Ajax设置
“withCredentials”: true
- 代理
想一下,如果我们请求的时候还是用前端的域名,然后有个东西帮我们把这个请求转发到真正的后端域名上,不就避免跨域了吗?这时候,Nginx出场了。可以在后端简单的配置一下:
server{
监听9099端口
listen 9099;
域名是localhost
server_name localhost;
#凡是localhost:9099/api这个样子的,都转发到真正的服务端地址http://localhost:9871
location ^~ /api {
proxy_pass http://localhost:9871;
}
}
前端就不用干什么事情了,除了写接口,也没后端什么事情了.
同源策略是针对浏览器端进行的限制,可以通过服务器端来解决该问题
DomainA客户端(浏览器) ==> DomainA服务器 ==> DomainB服务器 ==> DomainA客户端(浏览器) - postMessage
window.postMessage() 是HTML5的一个接口,专注实现不同窗口不同页面的跨域通讯。调用postMessage方法的window对象是指要接收消息的那一个window对象,该方法的第一个参数message为要发送的消息,类型只能为字符串;第二个参数targetOrigin用来限定接收消息的那个window对象所在的域,如果不想限定域,可以使用通配符 * 。
需要接收消息的window对象,可是通过监听自身的message事件来获取传过来的消息,消息内容储存在该事件对象的data属性中.