什么是跨域
在前端开发中,我们经常会遇到 跨域
问题,那么,什么是 跨域
呢?用大白话来讲,跨域
就是我们访问了别人的资源。比如说我们通过 img
link
script
或者 接口请求
等方式来访问别人的资源,都是属于 跨域
。
当碰见 跨域
时,浏览器会有固定格式的报错信息,如下
Access to XMLHttpRequest at 'xxx' from origin 'yyy' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
我们会发现,通过 img
linl
script
标签来请求别的资源时,好像都没有相关的 跨域
报错信息(有些资源会做跨域拦截,禁止访问),那是因为浏览器对这些由 标签元素
跨域请求限制不大,基本都是直接放行的。
那么,为啥会有 跨域
问题呢,以及是如何产生的呢?
产生跨域的原因
产生跨域的原因主要是来在 浏览器的同源策略
, 此处有两个重点 浏览器
和 同源
。
跨域
发生的位置是在 浏览器
,而非 服务器
。不同服务器之间请求资源是不存在跨域问题的。
浏览器为了保护用户的安全,使用 同源策略
阻止一些资源的解析和执行。
同源策略是一个重要的安全策略,它用于限制一个源的文档或者它加载的脚本如何能与另一个源的资源进行交互。
它能帮助阻隔恶意文档,减少可能被攻击的媒介。例如,它可以防止互联网上的恶意网站在浏览器中运行 JS 脚本,从第三方网络邮件服务(用户已登录)或公司内网(因没有公共 IP 地址而受到保护,不会被攻击者直接访问)读取数据,并将这些数据转发给攻击者。
什么是同源策略
我们先来看下一个域名的组成吧
同源指的是: 协议
域名
端口号
必须一致
源1 | 源2 | 是否同源 |
---|---|---|
a.com:80/b | a.com:80/b | 否 【协议不同】 |
a.com:80/b | c.com:80/b | 否 【域名不同】 |
a.com:80/b | a.com:81/b | 否 【端口不同】 |
a.com:80/b | a.com:80/b/c | 是 |
需要注意的一点,发生 跨域
时,浏览器会发起具体的请求,只是会对 跨域
请求进行报错提示,我们一起来看下具体的请求过程吧,如下图
跨域解决方案
1.CORS
CORS是 Cross Origin Resource Sharing
的缩写,翻译过来是 跨来源资源共享
CORS式一套机制,用于浏览器校验跨域请求。它的基本原理是:
只要服务器明确表示允许,则校验通过,服务器明确拒绝或没有表示,则校验不通过
这个也就决定了跨域目标服务器必须是自己的服务器
浏览器将CORS请求分为 简单请求
和 预检请求(也叫非简单请求)
简单请求
简单请求
需要满足以下两个条件
- 请求方法为:
HEAD
GET
POST
- HTTP的头信息不超出以下字段
- Accept
- Accept-Language
- Content-Language
- Last-Event-ID
- Content-type: 只限于三个值:
text/plain
multipart/form-data
application/x-www-form-urlencoded
请求过程
预检请求
不满足 简单请求
的都会转为 预检请求
请求过程
2.JSONP
JSONP是 JSON with Padding
的缩写
JSONP
是利用浏览器对标签的请求跨域限制较小的原理,实现的跨域
基本过程
jsonp整体工作流程(把jsonp封装到按钮点击事件里面)
- 浏览器: 声明函数接收服务器响应数据
- 浏览器: 点击按钮给页面添加script,添加额外参数callback=函数名
- 服务器 : 响应函数调用js代码 res.send('函数名(响应数据)')
- 浏览器: script标签会执行服务器响应的js
javascript
// 前端
function CallBack(res) {
console.log(res)
}
function JSONP_QUERY(url) {
const script = document.createElement("script");
script.src = url;
script.onload = function() {
script.remove()
}
document.body.appentChild(script);
}
document.querySelector("button").onclick = function() {
JSONP_QUERY("http://www.abc.com?callback=CallBack");
}
// 后端
app.get("/abc", (req, res) => {
const callback = req.query.callback;
res.send(`${callback}(${data})`)
})
3.代理
以上两个方案,有个致命的缺点,就是服务器必须是自己的,所有跨域的的接口请求必须自己来处理,除非让别人服务器去兼容跨域处理,显然是不可行的,碰到这种情况,那我们就需要通过代理服务器来处理,它的基本原理就是利用服务器之间的请求是不在跨域,允许直接请求。
基本过程