本文由图雀社区成员 灿若星空 写做而成,欢迎加入图雀社区,一块儿创做精彩的免费技术教程,予力编程行业发展。javascript
若是您以为咱们写得还不错,记得 点赞 + 关注 + 评论 三连🥰🥰🥰,鼓励咱们写出更好的教程💪前端
随着RESTful架构风格成为主流,以及Vue.js、React.js和Angular.js这三大前端框架的日益强大,愈来愈多的开发者开始由传统的MVC架构转向基于先后端分离这一基础架构来构建本身的系统,将前端页面和后端服务分别部署在不一样的域名之下。在此过程当中一个重要的问题就是跨域资源访问的问题,一般因为同域安全策略浏览器会拦截JavaScript脚本的跨域网络请求,这也就形成了系统上线时前端没法访问后端资源这一问题。笔者将结合自身开发经验,对这一问题产生的缘由以及相应的解决方案,给出详细介绍。java
同源策略,它是由Netscape提出的一个著名的安全策略。如今全部支持JavaScript 的浏览器都会使用这个策略。所谓同源是指:协议、域名、端口相同。node
举个例子:python
url | url | 是否同源 |
---|---|---|
test001.com/ | test001.com/ | 非同源 |
test001.com/ | test002.com/ | 非同源 |
test001.com:3000 | test001.com:4000 | 非同源 |
test001.com/userList | test001.com/orderList | 非同源 |
一个浏览器的两个tab页中分别打开来百度和谷歌的页面,当浏览器的百度tab页执行一个脚本的时候会检查这个脚本是属于哪一个页面的,即检查是否同源,只有和百度同源的脚本才会被执行。若是非同源,那么在请求数据时,浏览器会在控制台中报一个异常,提示拒绝访问。同源策略是浏览器的行为,是为了保护本地数据不被JavaScript代码获取回来的数据污染,所以拦截的是客户端发出的请求回来的数据接收,即请求发送了,服务器响应了,可是没法被浏览器接收。webpack
在前端开发阶段,一些框架的脚手架工具会使用webpack-dev-serve来代理数据请求,其本质上是一个基于node.js的网页服务器,因此感觉不到跨域资源访问的限制。web
当网站上线后,网页上不少资源都是要经过发送AJAX请求向服务器索要资源,可是在先后端分离的系统架构中,前端页面和后端服务每每不会部署在同一域名之下。好比用户经过浏览器访问 www.test001.com 这一地址,来到了系统首页,此时浏览器从网站服务器中只取回了基本的HTML页面以及CSS样式表文件和JavaScript脚本。系统首页的其余内容,好比轮播图、文章列表等,须要利用JavaScript脚本程序,向地址为 www.test002.com 的后端应用服务器发送请求来获取信息。此时因为浏览器的同源策略,该请求会被浏览器所拦截,这就形成了先后端数据不通这一结果。express
由于因为浏览器的同源策略,JavaScript脚本程序只能向同一域名下的服务器发送网络请求,那么能够经过网页服务器转发这一网络请求到相应的后端服务器,获取相关数据,而后网页服务器再把这一数据返回给浏览器。这一过程称之为反向代理。npm
假设用户经过地址www.test001.com访问到了系统首页,该系统首页中所加载的JavaScript脚步程序本应该要发送AJAX请求到www.test002.com/api/article…这一地址,来获取首页文章列表信息。此时应该改为向www.test001.com/api/article…这一与之同源的地址发送数据请求。该系统的网页服务器会收到此请求,而后代替JavaScript脚本程序向www.test002.com/api/article…这一地址请求数据,获取数据后将之返回给浏览器。此时JavaScript脚本程序就经过网页服务器这一桥梁成功获取到了后端应用服务器上的数据。编程
若服务器采用了宝塔面板这一管理软件,能够直接经过其提供的可视化界面进行反向代理的设置。对于一些新手而言,直接面对命令行进行各类操做,不够直观且难度较高,此时采用一些可视化的服务器管理软件是一个不错的选择。
如果喜欢用vim 直接在命令行里修改的同窗能够参考这篇博客
这个解决方案是否是有些眼熟呢?
浏览器的同源策略对JavaScript脚本向不一样域的服务器请求数据进行了限制,可是没有对HTML中的<script>标签进行限制,咱们能够基于此规则,动态建立<script>标签进行跨域资源访问。<script>标签中src这一属性值设置为:接口地址+处理数据的回调函数名称。相关代码示例以下:
<script>
var script = document.createElement('script');
script.type = 'text/javascript';
// 设置接口地址+数据获取成功后的回调函数(handleData)
script.src = 'http://www.test002.com/api/articleList&callback=handleData';
document.body.appendChild(script);
// 回调执行函数
function handleData(res) {
data = JSON.stringify(res)
console.log(data);
}
</script>
复制代码
在这里值得注意的是,由于请求数据的接口地址是写在了<script>标签中src这一属性值里面,那么数据请求的方式就只能支持GET请求,其余请求没法实现。在基于Vue.js这种框架开发的项目中,由于其使用了虚拟化DOM这一律念,JSONP跨域的方式对其并非一个很好的选择,对于原生JavaScript代码,能够采用此方式进行跨域。
跨域资源共享(CORS) 是一种机制,它使用额外的 HTTP 头来告诉浏览器 让运行在一个origin (domain)上的Web应用被准许访问来自不一样源服务器上的指定的资源。
出于安全缘由,浏览器限制从脚本内发起的跨源HTTP请求。 例如,XMLHttpRequest和Fetch API遵循同源策略。 这意味着使用这些API的Web应用程序只能从加载应用程序的同一个域请求HTTP资源,除非响应报文包含了正确CORS响应头! 因此要想实现跨域资源访问,这也就要求后端服务程序,应该根据CORS策略来配置好相应的HTTP响应头。
Access-Control-Allow-Origin: *
复制代码
表示该资源能够被任意外域访问。
若是服务端仅容许来自 test001.com 的访问,该首部字段的内容以下:
Access-Control-Allow-Origin: http://test001.com
复制代码
在 Node.js 的轻量级 Web 框架 Express 中,咱们只须要安装一个 cors 库并添加此中间件便可配置好跨域问题:
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 文档。
在以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方式须要后端来积极配合前端实现跨域。总之,没有技术银弹,咱们要在实际情形中比较分析,选择最合适的方案。
若是您以为咱们写得还不错,记得 点赞 + 关注 + 评论 三连🥰🥰🥰,鼓励咱们写出更好的教程💪
想要学习更多精彩的实战技术教程?来图雀社区逛逛吧。