首先 了解 同源策略
在制定Html规则时,为了安全的考虑,一个源的脚本(网页,网站)不能与另一个源的资源进行交互,
所以就引发一个词叫做“同源策略”。
同源策略:同源策略是一种约定,它是浏览器最核心的也最基本的安全功能,如果缺少了同源策略,则
浏览器的正常功能可能会受到影响。
同源:所谓同源(即指在同一个域),就是两个页面具有相同的协议(protocol),主机(host)和端口号(port)
什么是 跨域
当协议,主机,和端口号有一个不同时,就是跨域。
例如:

加载图片 CSS JS 可无视同源策略
<img src="跨域的图片地址" />
<link href="跨域的css地址" />
<script src="跨域的js地址"></script>
跨域会导致的问题
- Cookie,LocalStorage无法读取
- DOM和js对象无法读取,主要是iframe(可以说有iframe的对象无法读取)
- Ajax请求不能发送
解决跨域的方法
对最主要的AJAX跨域来说(也就是平常调接口时):
-
(后端)服务器配置CORS(跨域资源共享)
-
(后端)node.js或nginx,反向代理,把跨域改造成同域
-
(前端)将JSON升级成JSONP,在JSON的基础上,利用<script>标签可以跨域的特性,加上头设置
对iframe跨域来说:
- H5提供了postMessage()的方法,可以在父子页面进行通信(加分项)
No.1 JSONP
首先,我们要先明白 访问 www.baidu.com/,服务端一定会返回一个…? 服务端可以任意动态拼接数据返回,只要符合html格式要求。 同理于: <script src="xxxxxxxx/getData.js"> 服务端也许并不是返回一个getData.js静态文件,而是服务端通过拼接任何数据返回给你,只要拼接的数据格式不报错就行。
那么,我们已经知道了
<script> 可绕过跨域限制 服务器可以任意动态拼接数据返回 所以,<script>就可以获得跨域的数据,只要服务端愿意返回 以上即是JSONP的实现原理,概括如下:
在HTML标签里,一些标签比如script,img这样的获取资源的标签是没有跨域限制的。 动态插入script标签,通过script标签引入一个js文件,这个js文件载入成功后会执行我们在url参数中指定的函数,并且会把我们需要的json数据作为参数传入
优点: 兼容性好,简单易用,支持浏览器与服务器双向通信。
缺点: 只支持GET请求。
No.2 CORS
CORS是一个w3c标准,全称是"跨域资源共享"(Cross-origin resource sharing),当一个请求url的协议,域名,端口三者之间任意与当前页面地址不同即为跨域.它允许浏览器向跨源服务器发送XMLHttpRequest请求,从而克服AJAX只能同源使用的限制.
原理: 服务器对于CORS的支持,主要就是通过设置Access-Control-Allow-Origin来进行的。如果浏览器检测到相应的设置,就可以允许Ajax进行跨域的访问。 浏览器将CORS请求分为两类:简单请求和非简单请求 只要满足以下两大条件,就属于简单请求:
请求方法是以下三种方法之一:
- HEAD
- GET
- POST
HTTP的头信息不超出以下几种字段:
- Accept
- Accept-Language
- Content-Language
- Last-Event-ID
- Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain 不同时满足上面两个条件,就是非简单请求
代码简易示例: CORS - 服务器设置 http header
// 第二个参数填写允许跨域的域名称,不建议直接写"*"
response.setHeader("Access-Control-Allow-Origin","http://localhost:8080");
response.setHeader("Access-Control-Allow-Headers","X-Requested-With");
response.setHeader("Access-Control-Allow-Methods","PUT,POST,GET,DELETE,OPTIONS");
// 接收跨域的cookie
response.setHeader("Access-Control-Allow-Credentials","true");
No.3 通过修改document.domain来跨域
将子域和主域的document.domain设为同一个主域。 前提条件:这两个域名必须属于同一个基础域名,而且所用的协议,端口都要一致,否则无法使用document.domain来进行跨域。 详细信息请看
No.4 使用window.name来进行跨域
window对象有个name属性,该属性有个特征:即在一个窗口(window)的生命周期内,窗口载入的所有页面都是共享一个window.name的,每个页面对window.name都有读写的权限,window.name是持久存在一个窗口载入过的所有页面中的。
No.5 使用HTML5中新引进的window.postMessage方法来跨域传送数据
window.postMessage()是HTML5的一个接口,专注实现不同窗口不同页面的跨域通讯。
No.6 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'
}
})
No.7 代理 Nginx配置
server{
# 监听9099端口
listen 9099;
# 域名是localhost
server_name localhost;
#凡是localhost:9099/api这个样子的,都转发到真正的服务端地址http://localhost:9871
location ^~ /api {
proxy_pass http://localhost:9871;
}
}
// 请求的时候直接用回前端这边的域名http://localhost:9099,这就不会跨域,然后Nginx监听到凡是localhost:9099/api这个样子的,都转发到真正的服务端地址http://localhost:9871
fetch('http://localhost:9099/api/iframePost', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
msg: 'helloIframePost'
})
})