随着RESTful架构风格成为主流,以及Vue.js、React.js和Angular.js这三大前端框架的日益强大,越来越多的开发者开始由传统的MVC架构转向基于前后端分离这一基础架构来构建自己的系统,将前端页面和后端服务分别部署在不同的域名之下。在此过程中一个重要的问题就是跨域资源访问的问题,通常由于同域安全策略浏览器会拦截JavaScript脚本的跨域网络请求,这也就造成了系统上线时前端无法访问后端资源这一问题。笔者将结合自身开发经验,对这一问题产生的原因以及相应的解决方案,给出详细介绍。
同源策略,它是由Netscape提出的一个著名的安全策略。现在所有支持JavaScript 的浏览器都会使用这个策略。所谓同源是指:协议、域名、端口相同。
举个例子:
http://test001.com/与https://test001.com/ 协议不同 非同源
http://test001.com/与http://test002.com/ 域名不同 非同源
http://test001.com:3000与http://test001.com:4000 端口不同 非同源
http://test001.com/userList与http://test001.com/orderList 同源
一个浏览器的两个tab页中分别打开来百度和谷歌的页面,当浏览器的百度tab页执行一个脚本的时候会检查这个脚本是属于哪个页面的,即检查是否同源,只有和百度同源的脚本才会被执行。如果非同源,那么在请求数据时,浏览器会在控制台中报一个异常,提示拒绝访问。同源策略是浏览器的行为,是为了保护本地数据不被JavaScript代码获取回来的数据污染,因此拦截的是客户端发出的请求回来的数据接收,即请求发送了,服务器响应了,但是无法被浏览器接收。
在前端开发阶段,一些框架的脚手架工具会使用webpack-dev-serve来代理数据请求,其本质上是一个基于node.js的网页服务器,所以感受不到跨域资源访问的限制。
脚手架当网站上线后,网页上很多资源都是要通过发送AJAX请求向服务器索要资源,但是在前后端分离的系统架构中,前端页面和后端服务往往不会部署在同一域名之下。比如用户通过浏览器访问 http://www.test001.com 这一地址,来到了系统首页,此时浏览器从网站服务器中只取回了基本的HTML页面以及CSS样式表文件和JavaScript脚本。系统首页的其他内容,比如轮播图、文章列表等,需要利用JavaScript脚本程序,向地址为 http://www.test002.com 的后端应用服务器发送请求来获取信息。此时由于浏览器的同源策略,该请求会被浏览器所拦截,这就造成了前后端数据不通这一结果。
同源策略
因为由于浏览器的同源策略,JavaScript脚本程序只能向同一域名下的服务器发送网络请求,那么可以通过网页服务器转发这一网络请求到相应的后端服务器,获取相关数据,然后网页服务器再把这一数据返回给浏览器。这一过程称之为反向代理。
假设用户通过地址http://www.test001.com访问到了系统首页,该系统首页中所加载的JavaScript脚步程序本应该要发送AJAX请求到http://www.test002.com/api/articleList这一地址,来获取首页文章列表信息。此时应该改成向http://www.test001.com/api/articleList这一与之同源的地址发送数据请求。该系统的网页服务器会收到此请求,然后代替JavaScript脚本程序向http://www.test002.com/api/articleList这一地址请求数据,获取数据后将之返回给浏览器。此时JavaScript脚本程序就通过网页服务器这一桥梁成功获取到了后端应用服务器上的数据。
反向代理示意图
若服务器采用了宝塔面板[1]这一管理软件,可以直接通过其提供的可视化界面进行反向代理的设置。对于一些新手而言,直接面对命令行进行各种操作,不够直观且难度较高,此时采用一些可视化的服务器管理软件是一个不错的选择。
宝塔面板
若是喜欢用vim 直接在命令行里修改的同学可以参考这篇博客[2]
这个解决方案是不是有些眼熟呢?
浏览器的同源策略对JavaScript脚本向不同域的服务器请求数据进行了限制,但是没有对HTML中的script标签进行限制,我们可以基于此规则,动态创建script标签进行跨域资源访问。script标签中src这一属性值设置为:接口地址+处理数据的回调函数名称。相关代码示例如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>JSONP 跨域演示</title>
</head>
<body>
<script>
// 先定义好回调函数
function getarticleList(res) {
console.log(res);
}
var script = document.createElement('script');
script.type = 'text/javascript';
// 设置接口地址+数据获取成功后的回调函数(handleData)
script.src = 'http://localhost:3088/articleList&callback=getarticleList';
document.body.appendChild(script);
</script>
</body>
</html>
后端的话需要根据前端所传递过来的回调函数名称,把数据封装在此函数里面,这样在前端加载好数据后就自动调用了回调函数进行数据处理。(这是个骚操作)后端代码示例如下:
let http = require("http");
let querystring = require("querystring");
let server = http.createServer();
let articleList = JSON.stringify([
{
id: 1,
name: "平凡的世界"
},
{
id: 2,
name: "时间简史"
}
]);
server.on("request", function(req, res) {
console.log("收到请求了,请求路径是:" + req.url);
console.log(
"callback function name:",
JSON.stringify(querystring.parse(req.url))
);
let callbackFunctionName = querystring.parse(req.url).callback;
//根据回调函数名称,封装数据
let result = `${callbackFunctionName}(${articleList})`;
res.end(result);
});
server.listen(3088);
若有些朋友云里雾里的话,可以参考JSONP - 从理论到实践[3]此篇博文 在这里值得注意的是,因为请求数据的接口地址是写在了script标签中src这一属性值里面,那么数据请求的方式就只能支持GET请求,其他请求无法实现。在基于Vue.js这种框架开发的项目中,因为其使用了虚拟化DOM这一概念,JSONP跨域的方式对其并不是一个很好的选择,对于原生JavaScript代码,可以采用此方式进行跨域。
跨域资源共享(CORS) 是一种机制,它使用额外的 HTTP 头来告诉浏览器 让运行在一个origin (domain)上的Web应用被准许访问来自不同源服务器上的指定的资源。
出于安全原因,浏览器限制从脚本内发起的跨源HTTP请求。例如,XMLHttpRequest和Fetch API遵循同源策略。这意味着使用这些API的Web应用程序只能从加载应用程序的同一个域请求HTTP资源,除非响应报文包含了正确CORS响应头! 所以要想实现跨域资源访问,这也就要求后端服务程序,应该根据CORS策略来配置好相应的HTTP响应头。[4]
Access-Control-Allow-Origin: *
表示该资源可以被任意外域访问。
如果服务端仅允许来自 http://test001.com 的访问,该首部字段的内容如下:
Access-Control-Allow-Origin: http://test001.com
在 Node.js 的轻量级 Web 框架 Express 中,我们只需要安装一个 cors[5] 库并添加此中间件即可配置好跨域问题:
npm install cors
然后在 Express 应用中使用这个中间件:
var express = require('express')
var cors = require('cors')
var app = express()
app.use(cors())
app.get('/products/:id', function (req, res, next) {
res.json({msg: 'This is CORS-enabled for all origins!'})
})
app.listen(80, function () {
console.log('CORS-enabled web server listening on port 80')
})
通过这样的方式,就允许了所有的域名的请求方法。更多针对单个路由的跨域控制可以参见 cors[6] 文档。
在以SpringBoot为基础框架的应用程序中可以增加一个配置类进行CORS配置。具体代码如下所示:
@Configuration
public class WebConfig extends WebMvcConfigurationSupport {
@Autowired
AdminInterceptor adminInterceptor;
//配置跨域相关
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedMethods("*")
.allowedOrigins("*")
.allowedHeaders("*");
super.addCorsMappings(registry);
}
}
上述代码是较为粗犷的解决方案,即允许了所有域名的所有请求方法。在实际开发过程中应对于所收到请求的请求路径、请求方法、源、请求头加以限制,以确保服务的安全。继续以上述例子说明,安全的配置应该如下:
//配置跨域相关
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/**")
.allowedMethods("GET")
.allowedOrigins("www.test001.com")
.allowedHeaders("*");
super.addCorsMappings(registry);
}
在这种配置中只有来自域名www.test001.com才可以访问服务器数据,而且只接受GET方式的数据请求,对于访问路径也做了限制,只有/api开头的路径才能访问的到。这样就进一步保证了后端应用程序的安全性。
在以Flask这一轻量级web服务框架为基础所开发的应用服务中,首先要安装flask跨域资源共享库,可使用命令pip install flask_cors。接下来可按照如下代码进行CORS配置。
from flask_cors import CORS
app = Flask(__name__)
CORS(app, supports_credentials=True)
跨域问题在目前后端分离的架构中普遍存在,本文所介绍的这几种方案虽然都能够解决跨域问题,但其实各有优劣。比如Jsonp方式实现起来较为简单,但只支持GET请求方式,在原生JavaScript脚本中使用方便,但是当利用了如Vue.js这种MVVM框架时就有些难以施展了。反向代理的方式无需改动后端代码,但是对于整个系统而言可移植性较差,CORS方式需要后端来积极配合前端实现跨域。总之,没有技术银弹,我们要在实际情形中比较分析,选择最合适的方案。
本文由哈喽比特于4年以前收录,如有侵权请联系我们。
文章来源:https://mp.weixin.qq.com/s/aONJn-nGNGldBo666JL27w
京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。
日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。
据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。
今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。
日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。
近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。
据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。
9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...
9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。
据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。
特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。
据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。
近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。
据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。
9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。
《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。
近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。
社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”
2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。
罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。