早期为了防止CSRF(跨域请求伪造)的攻击,浏览器引入了同源策略(SOP)来提高安全性。而所谓"同源策略",即同域名(domain或ip)、同端口、同协议的才能互相获取资源,而不能访问其他域的资源。在同源策略影响下,一个域名A的网页可以获取域名B下的脚本,css,图片等,但是不能发送Ajax请求,也不能操作Cookie、LocalStorage等数据。同源策略的存在,一方面提高了网站的安全性,但同时在面对前后端分离、模拟测试等场景时,也带来了一些麻烦,从而不得不寻求一些方法来突破限制,获取资源。

虽然现在大多数项目都是后端进行的跨域配置,但是前端还是有很多跨域处理的方法的。

1.首先介绍的就是最优最常用的方案,Nginx反向代理;

所谓反向代理服务器,它是代理服务器中的一种。客户端直接发送请求给代理服务器,然后代理服务器会根据客户端的请求,从真实的资源服务器中获取资源返回给客户端。所以反向代理就隐藏了真实的服务器。利用这种特性,我们可以通过将其他域名的资源映射成自己的域名来规避开跨域问题。

1.1)在webpack配置文件 /config/index.js 里找到 proxyTable 开启代理 changeOrigin:true,

proxyTable: {

'/api':{

target:'http://xx.xx.xx.xx:8080',

changeOrigin:true,

pathRewrite:{

'^/api':'/api'

}

}

},

1.2)nginx 的 配置文件 xx.conf 的 server {} 里加如下:

location /api/ {

    # 把 /api 路径下的请求转发给真正的后端服务器
proxy_pass http://xx.xx.xx.xx:5568; # 把host头传过去,后端服务程序将收到your.domain.name, 否则收到的是localhost:8080
proxy_set_header Host $http_host; # 把cookie中的path部分从/api替换成/service
proxy_cookie_path /api /; # 把cookie的path部分从localhost:8080替换成your.domain.name
proxy_cookie_domain localhost:80 http://xx.xx.xx.xx:5568;
}

1.3)重新启动一下 nginx ,nginx reload

  1. JS跨域

利用浏览器的一些特性进行hack处理,从而避开同源策略的限制。由于同源策略不会阻止动态脚本的插入到文档中去,所以催生出了一种很常用的跨域方式: JSONP

假设,我们源页面是在a.com,想要获取b.com的数据,我们可以动态插入来源于b.com 的脚本:

script = document.createElement('script');

script.type = 'text/javascript';

script.src = 'http://www.b.com/getdata?callback=demo';

这里,我们利用动态脚本的src属性,变相地发送了一个http://www.b.com/getdata?callback=demo的GET请求。这时候,b.com页面接受到这个请求时,如果没有JSONP,会正常返回json的数据结果,像这样:

{ msg: 'helloworld' }

而利用JSONP,服务端会接受这个callback参数,然后用这个参数值包装要返回的数据:

demo({msg: 'helloworld'});

这时候,如果a.com的页面上正好有一个demo 的函数:

function demo(data) {

console.log(data.msg);

}

当远程数据一返回的时候,随着动态脚本的执行,这个demo函数就会被执行。

到这里,你应该能明白这个技术为什么叫JSONP了吧?就是因为使用这种技术服务器会接受回调函数名作为请求参数,并将JSON数据填充进回调函数中去。

不过一般在实际开发的时候,我们一般会利用jQuery对JSONP的支持,而避免手写很多代码。从1.2版本开始,jQuery中加入了对JSONP的支持,可以使用$.getJSON方法来请求跨域数据:

//callback后面的?会由jQuery自动生成方法名

$.getJSON('http://www.b.com/getdata?callback=?', function(data) {

console.log(data.msg);

});

还有一种更加常用的方法是,利用$.ajax方法,只要指定dataType为jsonp 即可:

$.ajax({

url: 'http://www.b.com/getdata?callback=?', //不指定回调名,可省略callback参数,会由jQuery自动生成

dataType: 'jsonp',

jsonpCallback: 'demo', //可省略

success: function(data) {

console.log(data.msg);

}

});

虽然JSONP在跨域ajax请求方面有很强的能力,但是它也有一些缺陷。首先,它没有关于JSONP调用的错误处理,一旦回调函数调用失败,浏览器会以静默失败的方式处理。其次,它只支持GET请求,这是由于该技术本身的特性所决定的。因此,对于一些需要对安全性有要求的跨域请求,JSONP的使用需要谨慎一点了。

3. CORS 方法。

"跨域资源共享"(Cross-origin resource sharing)是W3C出的一个标准。兼容性方面可以支持IE8+(IE8和IE9需要使用XDomainRequest对象来支持CORS),所以现在CORS也已经成为主流的跨域解决方案。

CORS的核心思想是通过一系列新增的HTTP头信息来实现服务器和客户端之间的通信。所以,要支持CORS,服务端都需要做好相应的配置,这样,在保证安全性的同时也更方便了前端的开发。

浏览器会将CORS请求分为两类:简单请求和非简单请求:

简单请求

在CORS标准中,会根据是否触发CORS preflight(预请求)来区分简单请求和非简单请求。

简单请求需要满足以下几个条件:

1.请求方法只允许:GET,HEAD,POST

2.对于请求头字段有严格的要求,一般情况下不会超过以下几个字段:

Accept

Accept-Language

Content-Language

Content-Type

3.当发起POST请求时,只允许Content-Type为application/x-www-form-urlencoded,multipart/form-data,text/plain。

对于简单请求来说,服务器和客户端之间的通信只是进行简单的交换。如图:

简单请求(来源:MDN)

浏览器发送一个带有Orgin字段的HTTP请求头,用来表明请求来源。服务器的Access-Control-Allow-Origin响应头表明该服务器允许哪些源的访问,一旦不匹配,浏览器就会拒绝资源的访问。大部分情况,大家都喜欢将Access-Control-Allow-Origin设置为*,即任意外域都能访问该资源。但是,还是推荐做好访问控制,以保证安全性。

非简单请求

对于非简单请求,情况就稍微复杂了点。在正式发送请求数据之前,浏览器会先发送一个带有'OPTIONS'方法的请求来确保该请求对于目标站点来说是安全的,这个请求也被称为”预请求“(preflight)。

浏览器和服务器之间具体的交互过程如图所示:

非简单请求

浏览器会在预检请求中,多发送两个字段Access-Control-Request-Method和Access-Control-Request-Headers,前者用于告知服务器实际请求所用的方法,后者用于告知服务器实际请求所携带的自定义请求首部字段。然后,服务器将根据请求头的信息来判断是否允许该请求。

针对非简单请求,服务器端可以设置几个相关字段:

Access-Control-Allow-Methods, 用来限制允许的方法名,

Access-Control-Allow-Header,用来限制允许的自定义字段名

Access-Control-Allow-Credentials,用来表明服务器是否允许credentials标志为true的场景。

Access-Control-Max-Age,用来表明预检请求响应的有效时间

Access-Control-Expose-Headers,用来指定服务器端允许的首部字段集合

另外,如果是在具体的实践过程中,调试OPTIONS请求可以使用

curl -X OPTIONS http://xxx.com

来进行查看相应头信息。也可以通过chrome://net-internals/#events来获取更加详细的网络请求信息。

优化 CORS

针对非简单请求来说,由于每个请求都会发送预请求,这就导致接口数据的返回会有所延迟,时间被加长。所以,在使用CORS的过程中,可以采用一些方案来优化请求,将非简单请求转换成简单请求,从而提高请求的速度。

1. 请求缓存

可以在服务器端使用Access-Control-Max-Age来缓存预请求的结果。从而提高网站性能。但是需要注意的是,大部分浏览器不会允许缓存‘OPTIONS‘请求太长时间,如:火狐是24小时(86400s),chromium是10分钟(600s)。

2.针对GET 请求

对于GET请求,没必要使用Content-Type, 尽可能地保持GET请求是简单请求。这样就可以减少Header上所携带的字段。从安全性上考虑,所有的API调用应该尽可能使用https协议,而这样可以将一些授权认证信息(如token)直接放在url中去,而不必放在头部。

3.针对POST 请求

对于POST请求,我们可以尽量使用FormData这种原生的格式:

function sendQuery(url, postData) {

let formData = new FormData();

for(var key in postData) {

formData.append(key, postData.key);

}

return fetch(url, {

body: formData,

headers: {

'Accept': '/'

},

method: 'POST'

});

}

sendQuery('http://www.xxx.com', {msg: 'hello'}).then(function(response) {

//do something with response

});

附带凭证信息的请求

CORS预请求会将用户的身份认证凭据排除在外,包括cookie、http-authentication报头等。如果需要支持用户凭证,需要在XHR的withCredentials属性设置为true,同时Access-control-allow-origin不能设置为*。在服务器端会利用响应报头Access-Control-Allow-Credentials来声明是否支持用户凭证。

同时,利用withCredentials这个属性,也可以检测浏览器是否支持CORS。下面创建一个带有兼容性处理的cors 请求:

function createCORSRequest(method, url){

var xhr = new XMLHttpRequest();

if ("withCredentials" in xhr){

xhr.open(method, url, true);

} else if (typeof XDomainRequest != "undefined"){

xhr = new XDomainRequest();

xhr.open(method, url);

} else {

xhr = null;

}

return xhr;

}

var request = createCORSRequest("POST", "http://www.xxx.com");

if (request){

request.onload = function(){

//do something with request.responseText

};

request.send();

}

如果浏览器支持fetch,则使用它做跨域请求更加方便:

fetch('http://www.xxx.com', {

method: 'POST',

mode: 'cors',

credentials: 'include' //接受凭证

}).then(function(response) {

//do something with response

});

参考原文::https://mp.weixin.qq.com/s?__biz=MjM5MTA1MjAxMQ==&mid=2651226366&idx=1&sn=4d9e6e7e5a38b0e2def53ed5c84f956e&chksm=bd49597a8a3ed06c7005ead8f1292ad8bb049edc1df093f33cf68cc87c1d25dd93006fe5014d&mpshare=1&scene=1&srcid=0725fufDap8DNf0dWbI5lh6l&key=d366dccab315fdbf9fab7c3bb2badfb6c7d1dfec5b55a47f5c66c79c344416ad34eb467b7f63c472b07b42e7f9bb0e5ede650f924defbb07c0dd399128e836842a72d1187909aac3aec6411208ed1a65&ascene=0&uin=MjcwODExNTI2MA%3D%3D&devicetype=iMac17%2C1+OSX+OSX+10.12.5+build(16F73)&version=12020610&nettype=WIFI&fontScale=100&pass_ticket=YcQhwBu05zAZhNyJn5xDbrEXnyVhFH9wW1OLvbltz%2FVQPIPR62KALUoIQI1dzxUU

前端跨域实现的几种方式?及使用Nginx反向代理配置。的更多相关文章

  1. System.Web.Http.Cors配置跨域访问的两种方式

    System.Web.Http.Cors配置跨域访问的两种方式 使用System.Web.Http.Cors配置跨域访问,众多大神已经发布了很多文章,我就不在详细描述了,作为小白我只说一下自己的使用心 ...

  2. Vue Nginx反向代理配置 解决生产环境跨域

    Vue本地代理举例: module.exports = { publicPath: './', devServer: { proxy: { '/api': { target: 'https://mov ...

  3. Nginx反向代理配置可跨域

    由于业务需要,同一项目中的前端代码放在静态环境中,而后端代码放在tomcat中,但此时问题却出现了:前端使用ajax请求后端获取数据时出现如下报错 XMLHttpRequest cannot load ...

  4. WEX5中ajax跨域访问的几种方式

    1.使用jsonp方式 使用jsonp访问的话,前端需要把回调函数名传递给后端,后端执行完后也需要把回调函数传回给前端,默认情况下ajax自动生成一个回调函数名,后端可以通过String callba ...

  5. 【js跨域】js实现跨域访问的几种方式

    这里说的js跨域是指通过js在不同的域之间进行数据传输或通信,比如用ajax向一个不同的域请求数据,或者通过js获取页面中不同域的框架中(iframe)的数据.只要协议.域名.端口有任何一个不同,都被 ...

  6. ajax实现跨域访问的两种方式

    一.使用jsonp实现跨域请求 在前端开发这中你会发现,所有带src属性的标签都可以跨域访问其他服务器文件.jsonp实现的原理也是如此. 以jsonp的数据类型进行请求时,JQ会动态在页面中添加sc ...

  7. 解决Entity Framework查询匿名对象后的跨域访问的一种方式

    在Entity Framework中,可以使用lambda表达式进行对数据的查询,而且可以将查询结果直接映射为对象或者对象列表,这极大的提高的开发速度,并且使数据层的数据更加方便处理和传递.但是很多时 ...

  8. html5的postmessage实现js前端跨域訪问及调用解决方式

    关于跨域訪问.使用JSONP的方法.我前面已经demo过了.详细见http://supercharles888.blog.51cto.com/609344/856886,HTML5提供了一个很强大的A ...

  9. SpringBoot:CORS处理跨域请求的三种方式

    一.跨域背景 1.1 何为跨域? Url的一般格式: 协议 + 域名(子域名 + 主域名) + 端口号 + 资源地址 示例: https://www.dustyblog.cn:8080/say/Hel ...

随机推荐

  1. 卷积实现 python

    import sys h, w = input().strip().split() h = int(h) w = int(w) img = [] for i in range(h): line = s ...

  2. 在python中创建列表的最佳和/或最快方法

    在python中,据我所知,至少有3到4种方法来创建和初始化给定大小的列表: 简单循环append: my_list = [] for i in range(50): my_list.append(0 ...

  3. es+mongodb 整合

    之前公司项目的数据都是从mysql查询,后面需求变更:同时技术上相应的也要改变策略,决定将mongodb和mysql的数据通过es建立索引来查询: 对于还没有接触或者真正了解es的可以先看一下相关Lu ...

  4. Spring学习笔记(8)——依赖注入

    spring依赖注入使用构造器注入使用属性setter方法注入使用Field注入(用于注解方式) 注入依赖对象可以采用手工装配或自动装配,在实际应用中建议使用手工装配,因为自动装配会产生未知情况,开发 ...

  5. Java的枚举类型使用方法详解

    1.背景在java语言中还没有引入枚举类型之前,表示枚举类型的常用模式是声明一组具有int常量.之前我们通常利用public final static 方法定义的代码如下,分别用1 表示春天,2表示夏 ...

  6. Nginx基础优化

    Nginx基础优化 1.隐藏nginx header版本号 1.1查看版本号 [root@Nginx ~]# curl -I http://www.yunwei.cn HTTP/1.1 200 OK ...

  7. centos7 实测 nagios 安装

    Nagios是一套开源的监控系统,可监控你的系统和网络.Nagios最新版本是Nagios Core 4.3.4,Nagios plugins 2.2.1.目前支持RHEL 7.x/6.x/5.x, ...

  8. windows server 2016 支持多用户远程登录

    服务器设置多用户同时远程桌面,可以提高访问效率,避免人多抢登服务器. 1. 首先需要先安装远程桌面服务  配置组策略,运行框输入gpedit.msc,打开计算机配置–>管理模板—>wind ...

  9. string参考

    #include <iostream> #include <string.h> class string { private: char *data; public: stri ...

  10. java序列化对象为什么要定义serialversionUID值?

    SerialVersionUid,简言之,其目的是序列化对象版本控制,有关各版本反序列化时是否兼容.如果在新版本中这个值修改了,新版本就不兼容旧版本,反序列化时会抛出InvalidClassExcep ...