前端跨域请求及解决方案

前言

本期文章的视频内容可以去 B 站看,视频内容有完整的代码演示

在 BS 架构的项目中以及前端开发编码过程中有可能会遇到跨域的问题,而跨域问题随着现代 web 开发很多项目采用前后端分离的方式进行分工合作再一次成为前端开发过程中都会遇到问题。曾经有一段时间 JSONP 的一度成为很多跨域问题的解决方案。在个人印象中曾经有一段时期各个公司关注于建站建官网进行 SEO,那个时候很多开发人员都会在前端粘贴一段脚本用来监控对网站的请求,那个时候跨域问题开始进入我个人的视野。后来随着 Vue、Angular、React 的兴起刮起了前端开发变革的风,前后分离的开发方式让跨域问题再一次成为需要面对的问题。
所谓跨域问题实际上就是浏览器的一个非常核心的安全策略,有很多方法可以来解决这个问题,各个方法使用场景各不相同。在项目中有有使用过几个解决方案,在这里做归纳总结

什么是跨域请求

在前端开发编码过程中,常见的 html 标签例如:aformimgscriptlinkiframe以及 Ajax 操作都可以指向一个资源地址或者说可以发起对一个资源的请求,那么这里所说的请求就存在同域请求还是跨域请求。
所谓跨域请求就是指:当前发起请求的域与该请求指向的资源所在的域不一致(这里所有的域是协议、域名和端口号的合集,同域就是所协议、域名和端口号均相同,任何一个不同都是跨域)。
常见的跨域场景有:

源 URL 请求 URL 是否跨域 说明
http://a.com/a http://a.com/b 同协议同域名通端口号,不同资源请求,不是跨域请求
http://a.com/a http://a.com:8080/b 端口不同
http://a.com/a https://a.com/b 协议不同
http://www.a.com/a http://x.a.com/b 主域名相同,但是子域名不相同
http://a.com/a http://b.com/b 域名不相同

同源策略(same-origin pllicy)

同源策略是由 Netscape 提出的一个著名的安全策略,它是浏览器最基本最核心的安全功能。如果缺少了同源策略,则浏览器的正常功能都会受到影响,可以说 web 是构建在同源策略基础上的,浏览器是针对同源策略的一种实现。
同源策略是浏览器的行为,是为了保护本地数据不被 Javascript 代码获取回来的数据污染,因此拦截的是客户端发出请求后回来的数据接收,即请求发送了,服务器响应了,但无法被浏览器接收执行,在现代浏览器中违反同源策略的跨域请求会在控制台直接报错。

[站外图片上传中...(image-8980a0-1561169393474)]

同源策略的具体表现:

  • DOM 层面的同源策略,限制了来自不同源的Document对象或者 JS 脚本对当前Document对象的读取或设置某些属性
  • Cookie 和 XMLHttpRequest 层面的同源策略,默认情况下禁止 Ajax 直接进行跨域请求(请求可以发起,但接收结果等操作被浏览器拦截并在控制台报错),cookie 层面数据默认不能跨域访问
  • 特定 HTML 标签的的策略,aformimgscriptlinkiframe等这些有src属性的标签可以对资源跨域访问和执行

为什么会有这些限制

同源策略是浏览器最核心基础的一个安全策略,是浏览器基于安全的需要来进行的限制。常见的CSRFXSS攻击都与之有关联。在这里就不做介绍,有兴趣的读者可以自行去搜索了解下这些攻击的原理

跨域请求为什么会出现在前端开发中?

既然同源策略是浏览器的核心基础安全策略,那为什么我们在进行前端开发特别是 Ajax 调用时还要进行跨域请求呢?同源策略是用来防御来自非法的攻击,但我们不能因为防御非法的攻击就将所有的跨域都拦截掉。

在现代前端开发中,我们经常需要调用第三方的服务接口(例如 mock server、fake api),随着专业化分工的出现有很多专业的信息服务提供商为前端开发者提供各类接口,这种情况下就需要进行跨域请求(这类前端接口服务很多是采用的 cors 方式来解决跨域问题的,下文会详细介绍)。

还有一类情况是在前后端分离的项目中,前端后端分属于不同的服务跨域问题在采用这种架构的时候就存在。而且现在很多项目都采用这种前后分离的方式,这类项目很多是会采用反向代理的方式来解决跨域问题。

Ajax 跨域请求如何实现

这里所说的跨域请求解决方案主要是针对 Ajax 请求

修改浏览器的安全设置(不推荐)

既然是浏览器的安全策略,那么最简单粗暴的方法就是禁用这个策略。这种操作很危险,不推荐使用。在此也不做具体的介绍,在跨域问题刚出来的时候有人采用这种方法,目前已很少有人采用。

JSONP

JSONP(JSON with Padding)是 JSON 的一种使用模式,可用于解决主流浏览器的跨域数据访问问题,在早两三年前端解决跨域问题中经常出现这类解决方案。
JSONP 的原理就是,Ajax 存在跨域安全问题但是 script 标签是不存在这类问题的,于是乎就有人根据这个特性做文章找解决方案。

remote.com/remote.js

remoteFunction('remote)

index.html

<script src="http://remote.com/remote.js" type="text/javascript"></script>
<script>
remoteFunction()
</script>

我们都知道这种方式是可以成功的,因此进一步改造:

remote.com/remote.js

let data = {
  code: 200,
  msg: "data from remote"
};
localFunction(data);

index.html

<script>
localFunction(data) {
  console.log(data)
}
</script>
<script src="http://remote.com/remote.js" type="text/javascript"></script>

通过这一步的改造就可以发现我们的本地函数的参数是来自于远程服务,在远程 remote.js 根据业务逻辑传递参数到本地函数进行的调用。

于是更进一步,将代码写死的 remote.js 进行动态的生成,我们以后台 PHP 语言为例:
remote.php

<?php
//通过设置content-type能够指明返回的内容类型
header('Contetn-type:application/json');

$callbackFunction = htmlspecialchars($_GET['callback']);
$data = 'data from remote';
echo $callbackFunction.'('.$data.')';

index.html

<script type="text/javascript">
  localFunction = function(data) {
    console.log(data);
  };
</script>
<script
  src="http://localhost:3000/remote.php?callback=localFunction"
  type="text/javascript"
></script>

这种实现方式就是 JSONP 的简单实现,JSONP 的核心理念就是利用 script 可以进行跨域请求,通过跨域请求将业务处理逻辑的回调函数通过 url 参数的形式发给远程请求,远程请求通过数据库调用获取到前端需要的数据后,将发送过来的回掉函数以及数据参数进行拼装,生成一段可执行的 JavaScript(json)代码,这样当这次请求完成后,对应的业务处理函数也就对应的执行了。

JSONP 有其天然的缺陷,因为是通过 script 的 src 进行的资源请求,所以都是 GET 方式,其他的 POST、PUT、DELETE 并不支持。这种采用这种解决方式越来越少。

跨域资源共享 CORS(Cross-Origin Resource Sharing)

CORS 是一个新的 W3C 标准,它新增的一组 HTTP 首部字段允许服务器其声明哪些来源请求有权限访问哪些资源,换言之它允许浏览器向其声明了 CORS 的站进行跨域请求。
这种方式最主要的特点就是会在响应的 HTTP 首部增加 Access-Control-Allow-Origin 等信息,从而判定哪些资源站可以进行跨域请求,还有几个其他相关的 HTTP 首部进行更加精细化的控制,最主要的还是 Access-Control-Allow-Origin。具体每个首部信息的含义可以去搜索详细了解下。
我们以 Express 搭建的远程服务为例来说明:

var express = require("express");
var cors = require("cors");
var app = express();

//使用express的cors中间件使其支持跨域请求
app.use(cors());

app.get("/", function(req, res, next) {
  res.json({ msg: "This is CORS-enabled for all origins!" });
});

app.listen(3000, function() {
  console.log("CORS-enabled web server listening on port 80");
});

针对支持 CORS 的服务发起 Ajax 请求最大的特定,客户端即浏览器首先会发送一次请求到服务端判断服务端是否支持跨域请求及是否合法,如果判断通过会回复信息给客户端浏览器,浏览器通过收到的回复信息判断服务端对这次跨域请求是否支持,如果支持就再发送实际的业务请求。所以在这里会有两次请求。
CORS 与 JSONP 对比来说优势比较明显,JSONP 只支持 GET 方式局限性很多,而且 JSONP 并不符合处理业务的正常流程。采用 CORS 的方式,前端编码与正常非跨域请求没有什么不同。在目前很多的 Fake API (模拟接口服务)、Mock Server(数据模拟服务)以及其他公共服务上都很多采用 CORS 的方式来解决跨域问题,例如 json-server 等。

iframe

iframe 与 JSONP 都是使用 src 属性没有跨域限制的特性,iframe 这种方式也不推荐使用,不做详细介绍。

反向代理

既然不能跨域请求,那么我们不跨域就可以了。通过在请求到达服务前部署一个服务,将接口请求进行转发,这就是反向代理。通过一定的转发规则可以将前端的请求转发到其他的服务。以 Nginx 为例:

server {
  listen 9999
  server_name localhost
  #将所有localhost:9099/api为开头的请求进行转发
  location ^~ /api {
    proxy_pass http://localhost:3000;
  }
}

通过反向代理我们将前端后端项目统一通过反向代理来提供对外的服务,这样在前端看上去就跟不存在跨域一样。
反向代理麻烦之处就在原对 Nginx 等反向代理服务的配置,在目前前后端分离的项目中很多都是采用这种方式。

总结

综上所述,CORS 和反向代理是目前使用最多的解决方案,这两个解决方案使用的场景并不相同,我们要根据自身的需求进行选择。公共服务、Fake API 、Mock Server 一般采用 CORS 的方案;而公司前后端分离的项目中更多是采用反向代理的方案。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,636评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,890评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,680评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,766评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,665评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,045评论 1 276
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,515评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,182评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,334评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,274评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,319评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,002评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,599评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,675评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,917评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,309评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,885评论 2 341