前后端分离项目,如何解决跨域问题?

前后端分离项目跨域问题是不可避免的。通常情况下前端由React、Vue等框架编写,通过ajax请求服务端API,传输数据用json格式。前后端分离项目,如何解决跨域问题?

那么为什么有跨域的问题呢?解决跨域问题有哪些方式?搞清楚这两个问题我们需要了解一下什么是同源策略。

浏览器的同源策略

同源策略(Same origin policy)是一种安全约定,是所有主流浏览器最核心也是最基本的安全功能之一。同源策略规定:不同域的客户端脚本在没有明确授权的情况下,不能请求对方的资源。同源指的是:域名、协议、端口均相同。

比如我们访问一个网站

http://www.test.com/index.html,

那么这个页面请求如下地址得情况是这样的:前后端分离项目,如何解决跨域问题?

另外,同源策略又分如下两种情况:

  1. DOM同源策略:禁止对不同源的页面DOM进行操作,主要防止iframe的情况。比如iframe标签里放一个支付宝付款的页面,如果没有同源策略,那么钓鱼网站除了域名不同,其他的则可以和支付宝的网站一模一样。

  2. XMLHttpRequest同源策略:禁止使用XHR对象向不同源的服务器发起http请求。比如网站记录了银行的cookie,这个时候你访问了恶意网站,黑客拿到你的cookie,再通过ajax请求之前的银行网站,便可以轻易的拿到你的银行信息。

所以,正是因为有了同源策略,大家的网络环境才相对的安全一些。

跨域问题的解决办法

了解了同源策略,就知道为什么会有跨域问题的产生了,都是为了安全。但是实际研发中,大家还是需要跨域去访问资源。典型的应用场景就是前后端分离的项目了。那么我们如何去解决跨域问题呢?

CORS-跨域资源共享

CORS是一种W3C标准,定义了当产生跨域问题的时候,客户端与服务端如何通信解决跨域问题。实际上就是前后端约定好定义一些自定义的http请求头,让客户端发起请求的时候能够让服务端识别出来该请求是过还是不过。

浏览器将CORS请求分为简单请求非简单请求:

简单请求

简单请求必须满足以下两个条件:

  1. 请求方式必须是HEAD、GET、POST三种方法之一。

  2. Http请求头必须只能是:Accept、Accept-Lanuage、Content-Lanuage、Last-Event-ID、Content-Type,其中Content-Type只限于三个值 application/x-www-form-urlencoded、multipart/form-data、text/plain。

非简单请求

不满足简单请求条件的就是非简单请求。针对非简单请求,浏览器会发起预检请求。预检请求的意思是当浏览器检查到你的页面含有跨域请求的时候,会发送一个OPTIONS请求给对应的服务器,以检测服务器是否允许当前域名的跨域请求。如果服务端允许该域名请求,则返回204或200状态码,浏览器接收到允许请求时候再继续发送对应的GET/POST/PUT/DELETE请求。同时服务器端也会告知浏览器预检请求的缓存时长是多少,在这个时间范围内,浏览器不会再次发起预检请求。

原理基本上就是上面说的这些,实际业务中我们如何通过配置来解决跨域问题呢?基本上常见的就是三种方式:

nginx配置

通常我们在nginx增加如下配置即可解决跨域问题:

前后端分离项目,如何解决跨域问题?

用nginx这种方式是最舒服的,不需要客户端和服务端多做其他工作,对代码无入侵。

jsonp

因为script标签是不受浏览器同源策略的影响,允许跨域请求资源(我们的每一个页面都引用了大量第三方js文件)。所以可以利用动态创建script标签,通过src属性发起跨域请求,这就是jsonp的原理。但是jsonp只支持GET请求,所以并不是一种好的方式。

服务端代码控制

可以在服务端增加对跨域请求的支持:

前后端分离项目,如何解决跨域问题?

这种方式相当于全局过滤器,对所有请求都过滤一遍。

以上三种方式都可以一定程度上解决跨域问题,但是nginx配置和服务端控制不能同时存在,否则会报“Access-Control-Allow-Origin Not Allow Multiple value”的错误。个人比较推荐nginx配置的方式,一劳永逸,不需要每个web项目都去编写跨域的代码。

大家在工作中有没有遇到过跨域问题呢?都是怎么解决的?欢迎评论区交流讨论,共同学习~

在前后端分离项目中,跨域问题很常见。我之前经历的较大的跨域问题技术栈为:spring boot + react 和 spring mvc + vue。这两条技术栈,在不同时期的生产环境都有实践的经历。针对跨域问题,最常见的有三种解决方案,即:服务端设置过滤器允许跨域访问,通过ngix反向代码解决跨域访问,使用jsonp支持跨域访问。下面我就这三点做简单阐述,如有其它方案,欢迎留言补充。

前后端分离项目,如何解决跨域问题?

服务端设置过滤器允许跨域访问。在spring boot 项目中,配置过滤器允许跨域访问的示例代码如下:

/**

* 全局跨域配置

*/

@Configuration

public class GlobalCorsConfig {

/**

* 允许跨域调用的过滤器

*/

@Bean

public CorsFilter corsFilter() {

CorsConfiguration config = new CorsConfiguration();

//允许所有域名进行跨域调用

config.addAllowedOrigin(“*”);

//允许跨越发送cookie

config.setAllowCredentials(true);

//放行全部原始头信息

config.addAllowedHeader(“*”);

//允许所有请求方法跨域调用

config.addAllowedMethod(“*”);

UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();

source.registerCorsConfiguration(“/**”, config);

return new CorsFilter(source);

}

}

在 Spring MVC 项目中配置跨域请求的示例代码如下:

public class SimpleCORSFilter implements Filter {

@Override

public void destroy() {

}

@Override

public void doFilter(ServletRequest req, ServletResponse res,

FilterChain chain) throws IOException, ServletException {

HttpServletResponse response = (HttpServletResponse) res;

response.setHeader(“Access-Control-Allow-Origin”, “*”);

response.setHeader(“Access-Control-Allow-Methods”, “POST, GET, OPTIONS, DELETE”);

response.setHeader(“Access-Control-Max-Age”, “3600”);

response.setHeader(“Access-Control-Allow-Headers”,” Origin, X-Requested-With, Content- Type, Accept”);

chain.doFilter(req, res);

}

@Override

public void init(FilterConfig arg0) throws ServletException {

}

}

<!– 跨域支持 –>

<bean class=”com.jd.vbbman.regular.comb.interceptor.SimpleCORSFilter”/>

前后端分离项目,如何解决跨域问题?

通过ngix反向代码解决跨域访问。Nginx解决跨域问题通过Nginx反向代理将对真实服务器的请求转移到本机服务器来避免浏览器的”同源策略限制”。如用户访问localhost,将被反向代理至 http://api.weiyou.com

server {

listen 80;

server_name localhost;

## 用户访问 localhost,则反向代理到 http://api.weiyou.com

location / {

root html;

index index.html index.htm;

proxy_pass http://api.weiyou.com;

}

}

jsonp是利用script标签可以引入外部资源,并将引入的外部资源作为js代码解析的特性,获取跨域资源。

// jsonp封装

function jsonp({url,data={},seccess}){

var str = ”;

for(var i in data){

str += `${i}=${data[i]}&`;

}

var d = new Date();

var script = document.createElement(‘script’);

script.src = url + ‘?’ + str + ‘__qft’ + d.getTime();

document.body.appendChild(script);

window[data[data.fnName]] = function(res){

seccess(res);

}

}

//调用jsonp

jsonp(请求的地址,{发送数据的名字:发送数据值,发送数据的名字2:发送数据值2,…..},

function(res){

console.log(res);

}

)

前后端分离项目,如何解决跨域问题?

以上三种方案,仅对 Java 服务端的过滤器配置解决方案有实践,其余两种是找寻的答案,其示例代码的正确性不能确认。

作者:夕阳雨晴,欢迎关注我的头条号:偶尔美文,主流Java,为你讲述不一样的码农生活。

前后端分离项目,如何解决跨域问题?

现在开发项目,大部分公司采用的都是前后端分离的方式进行开发,由于现在产品形态越来越多,网页、手机端、桌面端等等,为了面对各种端,数据中心化、微服务概念的出现,我们为了集成这些服务,不得不去面对一个常见的问题——解决跨域请求的问题。

以前工作开发中,经常会有这样的问题,前端工程师的前端页面由于跨域问题报错了,来协调后端开发人员解决,后台开发人员还那解释你来看我这边的接口是正常的,应该是你的问题,这是前端开发人员的心顿时是崩塌的,如果你还不知道怎么办的时候,也许会默默的自己去寻找解决方案,一查解决方案,这个工作应该需要前后台一起配合,你还得给后端开发人员去好说歹说,让他们也看看一起解决。我很能理解作为前端的我们真是不容易啊。

前后端分离项目,如何解决跨域问题?

关于跨域这个问题,不仅前端工程师需要了解,后端工程师也需要了解更应该重视,因为后面会提及到相关的解决方案,需要共同配合才能完成。借着回答这个问题的机会,我来把跨域的相关内容进行系统的梳理,分享给大家。

什么是跨域

前后端分离项目,如何解决跨域问题?

跨域(CORS)——跨源资源共享。换成我们前端开发人员能理解的就是指浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器对JavaScript的实施的安全限制。

换个通俗的比方来说,比如经常会有一些模仿金融机构的钓鱼网站,用了和金融机构类似的域名,你点击进去一看,竟然和你熟悉的金融网站一模一样,如果你没注意域名的差别,如果你在网站上输入了卡号和密码信息那就很危险了,有可能这个网站是frame了金融机构的网站,如果金融网站没有做相关的安全限制,你的信息完全有可能被非法分子获取。由此可见浏览器的同源策略存在是十分有必要的。

我顺便在给大家介绍下如何区分是否是同源,所谓的同源是指,域名,协议,端口均为相同。接下来举几个示例,方便大家进行理解:

前后端分离项目,如何解决跨域问题?

常用方法一:使用 JSONP 进行 Get 请求

这应该是我们接触到的第一个解决跨域的方法,笔者记得前端入门经典红皮书里有过介绍,JSONP有两部分主成:回调函数和数据。回调函数是当响应完成在页面中调用的函数,回调函数的名字一般在请求中进行制定。而数据就是传入回调函调函数中的JSON数据。为了解释这个,还是我们来看下面这个例子吧:

比如我们来实现一个获取当地天气数据的功能,我们需要在后端与天气接口平台交互获取天气数据,前端页面通过GET后端API的方式获取天气信息。

1、首先定义我们前端页面的回调函数功能,我们定义了一个gotWeather的函数:

前后端分离项目,如何解决跨域问题?

2、接下来定义请求方法,请注意callback后面的参数和回调函数保持一致的名字:

前后端分离项目,如何解决跨域问题?

3、我们后台接口最终要返回非类似这样的数据内容:

前后端分离项目,如何解决跨域问题?

你会发现,数据能够正常返回,你也许会问为什么这样可以,不违背同源原则吗?其实之所以有效,并且不违反安全性,因为这是经过前后端共同协作,约定以这种方式传递数据。但是你会发现使用这种方法会有一个问题是,只能用于Get请求。

常用方法二:跨域资源共享(CORS)请求方式

目前这种方式用的比较多,应用比较广泛,如果你的项目受部署环境限制的话,建议还是用这种。

前后端分离项目,如何解决跨域问题?

1、什么是CORS?

CORS是一个W3C标准,全称是“跨域资源共享”(跨源资源共享)。它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。

CORS需要浏览器和服务器同时支持目前,所有浏览器都支持该功能,IE浏览器不能低于IE10.IE8 +:IE8 / 9需要使用XDomainRequest对象来支持CORS。

整个CORS通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS通信与同源的AJAX通信没有差别,代码完全一样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。因此,实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信。

CORS 请求分成两类:简单请求(simple request)非简单请求(not-so-simple request)

2、什么是简单请求?

2.1、首先介绍下什么是简单请求,请求方法是以下请求方法:

  • Head

  • Get

  • Post

2.2、HTTP 的头信息不超出以下几种字段:

  • Accept

  • Accept-Language

  • Content-Language

  • Last-Event-ID

  • Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain

凡是不同时满足上面两个条件,就属于非简单请求。一句话,简单请求就是简单的 HTTP 方法与简单的 HTTP 头信息的结合。

2.2、简单请求的大致流程我做下解释:

加入我们的一个网站页面地址需要去请求一个服务端的API,这个页面的请求头可能是这样的:

前后端分离项目,如何解决跨域问题?

上面的头信息中,Origin字段用来说明,本次请求来自哪个域(协议 + 域名 + 端口)。服务器根据这个值,决定是否同意这次请求。

如果Origin指定的源,不在许可范围内,服务器会返回一个正常的 HTTP 回应。浏览器发现,这个回应的头信息没有包含Access-Control-Allow-Origin字段,就知道出错了,从而抛出一个错误,被XMLHttpRequest的onerror回调函数捕获。注意,这种错误无法通过状态码识别,因为 HTTP 回应的状态码有可能是200。

如果Origin指定的域名在许可范围内,服务器返回的响应,会多出几个头信息字段。具体的请求交互流程如下图所示:

前后端分离项目,如何解决跨域问题?

如果一切顺利正常的话,你就会看到服务端一些返回的头信息

前后端分离项目,如何解决跨域问题?

3、什么是非简单请求

3.1、 简单的介绍下什么是非简单请求(not-so-simple request)

非简单请求是那种对服务器提出特殊要求的请求,比如请求方法是PUT或DELETE,或者Content-Type字段的类型是application/json。

非简单请求的 CORS 请求,会在正式通信之前,增加一次 HTTP 查询请求,称为“预检”请求(preflight)。浏览器先询问服务器,当前网页所在的域名是否在服务器的许可名单之中,以及可以使用哪些 HTTP 动词和头信息字段。只有得到肯定答复,浏览器才会发出正式的XMLHttpRequest请求,否则就报错。这是为了防止这些新增的请求,对传统的没有 CORS 支持的服务器形成压力,给服务器一个提前拒绝的机会,这样可以防止服务器大量收到DELETE和PUT请求,这些传统的表单不可能跨域发出的请求。

3.2、通过示例,我们来了解其实现的原理

3.2.1、比如我们在前端页面的请求代码时这样的如下所示:

前后端分离项目,如何解决跨域问题?

上面代码中,HTTP 请求的方法是PUT,并且发送一个自定义头信息X-Custom-Header。

3.2.2、浏览器发现,这是一个非简单请求,就自动发出一个“预检”请求,要求服务器确认可以这样请求。下面是这个“预检”请求的 HTTP 头信息。

前后端分离项目,如何解决跨域问题?

“预检”请求用的请求方法是OPTIONS,表示这个请求是用来询问的。头信息里面,关键字段是Origin,表示请求来自哪个源。

除了Origin字段,“预检”请求的头信息包括两个特殊字段。

(1)Access-Control-Request-Method 该字段是必须的,用来列出浏览器的 CORS 请求会用到哪些 HTTP 方法,上例是PUT。

(2)Access-Control-Request-Headers 该字段是一个逗号分隔的字符串,指定浏览器 CORS 请求会额外发送的头信息字段,上例是X-Custom-Header。

3.3、服务器收到“预检”请求以后,检查了Origin、Access-Control-Request-Method和Access-Control-Request-Headers字段以后,确认允许跨源请求,就可以做出回应。

前后端分离项目,如何解决跨域问题?

3.4 一旦服务器通过了“预检”请求,以后每次浏览器正常的 CORS 请求,就都跟简单请求一样,会有一个Origin头信息字段。服务器的回应,也都会有一个Access-Control-Allow-Origin头信息字段。

3.4 文字内容有点多,把刚才描述的内容用一张流程图表述下,大家会清晰许多,如下所示:

前后端分离项目,如何解决跨域问题?

4、与 JSONP 的比较

CORS 与 JSONP 的使用目的相同,但是比 JSONP 更强大。JSONP 只支持GET请求,CORS 支持所有类型的 HTTP 请求。JSONP 的优势在于支持老式浏览器,以及可以向不支持 CORS 的网站请求数据。

5、接下来给后端开发人员分享下如何配置跨域请求


5.1 PHP 简单示例

前后端分离项目,如何解决跨域问题?

5.2 Node 开发人员使用 Express 简单示例:

5.2.1 首先安装 cors 中间件:

npm install cors

5.2.3 然后配置比如入口文件,server/

index.js

前后端分离项目,如何解决跨域问题?

5.2.4 你可以对跨域进行配置,如下图所示:

前后端分离项目,如何解决跨域问题?

5.2.5 你可以做个请求示例尝试下,如果一切正常,你可以在 web 开发者工具中看到如下所示:

前后端分离项目,如何解决跨域问题?

java 的由于我不太熟,可以自行解决方案,原理和 PHP 的道理是差不多的。

常用方法三:nginx 反向代理

这个方法应用也十分广泛,也是十分常见的,这也需要服务端配合下面还是用一段Ngxin配置来说明这个问题,如下图所示:

前后端分离项目,如何解决跨域问题?

实现原理类似于Node中间件代理,需要你搭建一个中转nginx服务器,用于转发请求。使用nginx反向代理实现跨域,是最简单的跨域方式。只需要修改nginx的配置即可解决跨域问题,支持所有浏览器,支持session,不需要修改任何代码,并且不会影响服务器性能。实现思路:通过nginx配置一个代理服务器(域名与domain1相同,端口不同)做跳板机,反向代理访问domain2接口,并且可以顺便修改cookie中domain信息,方便当前域cookie写入,实现跨域登录。

小节

以上是解决跨域问题最常用的三种方式,应该能解决你业务中遇到的问题,有点需需要提示的是方法二和方法三不要混着用,否则会报“Access-Control-Allow-Origin Not Allow Multiple value”的错误,我推荐大家用方法三使用nginx反向代理做跨域解决方案,比较简单和直接,可谓一劳永逸。当然跨域的方法还有其他的,比如使用WebSocket、postMessage API 、各种 iframe 的解决方案,由于不太常用和篇幅问题原因,就不再一一介绍了,感兴趣的小伙伴们可以自行搜索。

如果你觉得本回答分享对你有所帮助,欢迎给个赞?支持下,分享出去让更多的人知道,如果你有其它的方法,欢迎再留言区进行分享。

注:本回答第二部部分参考阮一峰的《JavaScript 标准参考教程(alpha)》

更多前端内容分享,欢迎关注「前端达人」及其微信公众号

很高兴能够看到和回答这个问题!

总工程师负责准备HTML页面,完成主页的设计。协调工程师采用模板法将html页面转换为页面aspx,并内置代码Asp.net);前台和后台的支持力度大,后台要等到开发初期将html很好的转换为aspx。如果需求发生变化,需要修改HTML模块,也需要修改aspx模块。这是一个与降低开发效率密切相关的问题。在交付产品的时候,一定要把前后所有的代码打包,把代码放在同一个服务器上,或者干脆放在静态模式下。

前后端分离项目,如何解决跨域问题?

什么是跨域?

浏览器端请求不同域名的资源浏览器端

只是浏览器的安全限制策略,CURL无此问题不同域名

子域名也算,端口不同也算注意:Cookie只区分域名,不区分端口!

前后端分离项目,如何解决跨域问题?

前后端分离项目,如何解决跨域问题?

解决方案一:JSONP原理

HTML中的<script>标签可以跨域加载,加载完成后立即执行。

<script src=”XXXX/XXXX.js”></script>

实现:

准备一个全局Function,比如callback1230动态增加一个<script src=”API地址?callback=callback123″></script>

返回的内容:callback123({DATA..…})

前后端分离项目,如何解决跨域问题?

解决方案2:CORS原理

W3C新标准:

响应的头中只要包含Access-Control-Allow-Origin:域名1,域名2浏览器对这些域名就不会限制跨域

还可以使用通配符*

实现:

Server或nginx加Header

前后端分离项目,如何解决跨域问题?

解决方案2:共享登录态

Session存Cookie(加密),挂在livechart.cn的根域名下2个加密字段:userld,userldMd5用于其它后台服务解析登录态,前台无法解析1个公开字段:userName只影响前台显示,不影响任何后台逻辑。

前后端分离项目,如何解决跨域问题?

前后办公已经成为互联网开发项目的标准使用形式。传统的混合开发模式虽然经受住了考验,仍然可以支撑应用的开发。但在未来,社会分工将更加细化,开发前后分离是必然趋势。此外,前后端分离将为后续大规模分布式结构、微服务结构、多极服务(如浏览器、凤尾鱼、I0S、终端等)打下坚实基础。

以上便是我的一些见解和回答,可能不能如您所愿,但我真心希望能够对您有所帮助!不清楚的地方您还可以关注我的头条号“每日精彩科技”我将竭尽所知帮助您!

码字不易,感觉写的还行的话,还请点个赞哦!

前后端分离项目,如何解决跨域问题?

一,什么是跨域,怎么多了一个OPTIONS请求?

二,服务端如何响应OPTIONS请求,告诉客户端是否允许跨域?

三,开源项目实例代码


一,什么是跨域,怎么多了一个OPTIONS请求?

在前后端分离架构下,客户端调用API接口服务,获取数据何服务端完成交互。如果使用浏览器查看API调用信息,就会发现多了一个OPTIONS接口请求。

OPTIONS请求是做什么用的呢?

在跨域资源共享时,为了保障服务端安全,于是出现了CORS,一个W3C标准,定义了当产生跨域问题的时候,客户端与服务端如何进行通信来解决跨域问题。

CORS标准约定了前后端增加的自定义HTTP请求头,当客户端发起请求时,服务端进行识别,决定该请求是否被允许。

获取是不是被允许调用的这次请求,就是客户端OPTIONS请求,通常由浏览器自动发起,不需要自己写代码实现调用逻辑。

前后端分离项目,如何解决跨域问题?

二,服务端如何响应OPTIONS请求,告诉客户端是否允许跨域?

有回答给出了Nginx添加请求头信息的实现方式,这里针对服务端代码实现给出具体方法。

/**
* 对跨域提供支持
*/
@Override
protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
httpServletResponse.setHeader(“Access-control-Allow-Origin”, httpServletRequest.getHeader(“Origin”));
httpServletResponse.setHeader(“Access-Control-Allow-Methods”, “GET,POST,OPTIONS,PUT,DELETE”);
httpServletResponse.setHeader(“Access-Control-Allow-Headers”, httpServletRequest.getHeader(“Access-Control-Request-Headers”));

// 是否允许发送Cookie,默认Cookie不包括在CORS请求之中。设为true时,表示服务器允许Cookie包含在请求中。
httpServletResponse.setHeader(“Access-Control-Allow-Credentials”, “true”);

// 跨域时会首先发送一个option请求,这里我们给option请求直接返回正常状态
if (httpServletRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {
httpServletResponse.setStatus(HttpStatus.OK.value());
return false;
}

return super.preHandle(request, response);
}

代码要点:返回允许前端调用时传递的请求头、Cookie、域名等信息。

前后端分离项目,如何解决跨域问题?

三,开源项目实例代码

JeecgBoot低代码开发框架,基于代码生成器,快速开发业务应用系统,开源免费,Java + Spring Boot全家桶。

开源协议: Apache-2.0

项目地址:代码托管在Github和Gitee,国内用户使用Gitee下载速度快

系统环境:Win10/Ubuntu,JDK8,maven,node,docker

依赖服务:MySQL,Redis,ActiveMQ

前后端分离项目,如何解决跨域问题?


我是源码控,Jext技术社区创始人,创业公司CTO,参与2个开源项目,技术专栏作者,输出技术服务,帮助早期初创团队1个月上线MVP,快速验证商业模式。

1,分享软件研发知识和经验

2,搭建和管理技术团队,高效率,高产出

3,实践软件工程理念,实践云原生和DevOps,快速上线MVP验证商业模式

前后端分离项目,如何解决跨域问题?

前后端分离项目,如何解决跨域问题?

前后端分离项目,如何解决跨域问题?

前后端分离项目,如何解决跨域问题?

前后端分离项目,如何解决跨域问题?

前言

从刚接触前端开发起,跨域这个词就一直以很高的频率在身边重复出现,一直到现在,已经调试过N个跨域相关的问题了,16年时也整理过一篇相关文章,但是感觉还是差了点什么,于是现在重新梳理了一下。

个人见识有限,如有差错,请多多见谅,欢迎提出issue,另外看到这个标题,请勿喷~

题纲

关于跨域,有N种类型,本文只专注于ajax请求跨域(,ajax跨域只是属于浏览器”同源策略”中的一部分,其它的还有Cookie跨域iframe跨域,LocalStorage跨域等这里不做介绍),内容大概如下:

  • 什么是ajax跨域原理表现(整理了一些遇到的问题以及解决方案)
  • 如何解决ajax跨域JSONP方式CORS方式代理请求方式
  • 如何分析ajax跨域http抓包的分析一些示例

什么是ajax跨域

ajax跨域的原理

ajax出现请求跨域错误问题,主要原因就是因为浏览器的“同源策略”,可以参考

浏览器同源政策及其规避方法(阮一峰)

CORS请求原理

CORS是一个W3C标准,全称是”跨域资源共享”(Cross-origin resource sharing)。它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。

基本上目前所有的浏览器都实现了CORS标准,其实目前几乎所有的浏览器ajax请求都是基于CORS机制的,只不过可能平时前端开发人员并不关心而已(所以说其实现在CORS解决方案主要是考虑后台该如何实现的问题)。

关于CORS,强烈推荐阅读
跨域资源共享 CORS 详解(阮一峰)

另外,这里也整理了一个实现原理图(简化版):

前后端分离项目,如何解决跨域问题?

如何判断是否是简单请求?

浏览器将CORS请求分成两类:简单请求(simple request)和非简单请求(not-so-simple request)。只要同时满足以下两大条件,就属于简单请求。

  • 请求方法是以下三种方法之一:HEAD,GET,POST
  • HTTP的头信息不超出以下几种字段:AcceptAccept-LanguageContent-LanguageLast-Event-IDContent-Type(只限于三个值application/x-www-form-urlencoded、 multipart/form-data、text/plain)

凡是不同时满足上面两个条件,就属于非简单请求。

ajax跨域的表现

说实话,当初整理过一篇文章,然后作为了一个解决方案,但是后来发现仍然有很多人还是不会。无奈只能耗时又耗力的调试。然而就算是我来分析,也只会根据对应的表现来判断是否是跨域,因此这一点是很重要的。

ajax请求时,如果存在跨域现象,并且没有进行解决,会有如下表现:(注意,是ajax请求,请不要说为什么http请求可以,而ajax不行,因为ajax是伴随着跨域的,所以仅仅是http请求ok是不行的)

注意:具体的后端跨域配置请看题纲位置。

第一种现象:No ‘Access-Control-Allow-Origin’ header is present on the requested resource,并且The response had HTTP status code 404

前后端分离项目,如何解决跨域问题?

出现这种情况的原因如下:

  • 本次ajax请求是“非简单请求”,所以请求前会发送一次预检请求(OPTIONS)
  • 服务器端后台接口没有允许OPTIONS请求,导致无法找到对应接口地址

解决方案: 后端允许options请求

第二种现象:No ‘Access-Control-Allow-Origin’ header is present on the requested resource,并且The response had HTTP status code 405

前后端分离项目,如何解决跨域问题?

这种现象和第一种有区别,这种情况下,后台方法允许OPTIONS请求,但是一些配置文件中(如安全配置),阻止了OPTIONS请求,才会导致这个现象

解决方案: 后端关闭对应的安全配置

第三种现象:No ‘Access-Control-Allow-Origin’ header is present on the requested resource,并且status 200

前后端分离项目,如何解决跨域问题?

这种现象和第一种和第二种有区别,这种情况下,服务器端后台允许OPTIONS请求,并且接口也允许OPTIONS请求,但是头部匹配时出现不匹配现象

比如origin头部检查不匹配,比如少了一些头部的支持(如常见的X-Requested-With头部),然后服务端就会将response返回给前端,前端检测到这个后就触发XHR.onerror,导致前端控制台报错

解决方案: 后端增加对应的头部支持

第四种现象:heade contains multiple values ‘*,*’

前后端分离项目,如何解决跨域问题?

表现现象是,后台响应的http头部信息有两个Access-Control-Allow-Origin:*

说实话,这种问题出现的主要原因就是进行跨域配置的人不了解原理,导致了重复配置,如:

  • 常见于.net后台(一般在web.config中配置了一次origin,然后代码中又手动添加了一次origin(比如代码手动设置了返回*))
  • 常见于.net后台(在IIS和项目的webconfig中同时设置Origin:*)

解决方案(一一对应):

  • 建议删除代码中手动添加的*,只用项目配置中的即可
  • 建议删除IIS下的配置*,只用项目配置中的即可

如何解决ajax跨域

一般ajax跨域解决就是通过JSONP解决或者CORS解决,如以下:(注意,现在已经几乎不会再使用JSONP了,所以JSONP了解下即可)

JSONP方式解决跨域问题

jsonp解决跨域问题是一个比较古老的方案(实际中不推荐使用),这里做简单介绍(实际项目中如果要使用JSONP,一般会使用JQ等对JSONP进行了封装的类库来进行ajax请求)

实现原理

JSONP之所以能够用来解决跨域方案,主要是因为 <script> 脚本拥有跨域能力,而JSONP正是利用这一点来实现。具体原理如图

前后端分离项目,如何解决跨域问题?

实现流程

JSONP的实现步骤大致如下(参考了来源中的文章)

  • 客户端网页网页通过添加一个<script>元素,向服务器请求JSON数据,这种做法不受同源政策限制function addScriptTag(src) { var script = document.createElement(‘script’); script.setAttribute(“type”,”text/javascript”); script.src = src; document.body.appendChild(script); } window.onload = function () { addScriptTag(‘http://example.com/ip?callback=foo’); } function foo(data) { console.log(‘response data: ‘ + JSON.stringify(data)); }; 请求时,接口地址是作为构建出的脚本标签的src的,这样,当脚本标签构建出来时,最终的src是接口返回的内容
  • 服务端对应的接口在返回参数外面添加函数包裹层

foo({ “test”: “testData” });

  • 由于<script>元素请求的脚本,直接作为代码运行。这时,只要浏览器定义了foo函数,该函数就会立即调用。作为参数的JSON数据被视为JavaScript对象,而不是字符串,因此避免了使用JSON.parse的步骤。

注意,一般的JSONP接口和普通接口返回数据是有区别的,所以接口如果要做JSONO兼容,需要进行判断是否有对应callback关键字参数,如果有则是JSONP请求,返回JSONP数据,否则返回普通数据

使用注意

基于JSONP的实现原理,所以JSONP只能是“GET”请求,不能进行较为复杂的POST和其它请求,所以遇到那种情况,就得参考下面的CORS解决跨域了(所以如今它也基本被淘汰了)

CORS解决跨域问题

CORS的原理上文中已经介绍了,这里主要介绍的是,实际项目中,后端应该如何配置以解决问题(因为大量项目实践都是由后端进行解决的),这里整理了一些常见的后端解决方案:

PHP后台配置

PHP后台得配置几乎是所有后台中最为简单的,遵循如下步骤即可:

  • 第一步:配置Php 后台允许跨域

<?php header(‘Access-Control-Allow-Origin: *’); header(‘Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept’); //主要为跨域CORS配置的两大基本信息,Origin和headers

  • 第二步:配置Apache web服务器跨域(httpd.conf中)

原始代码

<Directory /> AllowOverride none Require all denied </Directory>

改为以下代码

<Directory /> Options FollowSymLinks AllowOverride none Order deny,allow Allow from all </Directory>

Node.js后台配置(express框架)

Node.js的后台也相对来说比较简单就可以进行配置。只需用express如下配置:

app.all(‘*’, function(req, res, next) { res.header(“Access-Control-Allow-Origin”, “*”); res.header(“Access-Control-Allow-Headers”, “X-Requested-With”); res.header(“Access-Control-Allow-Methods”, “PUT,POST,GET,DELETE,OPTIONS”); res.header(“X-Powered-By”, ‘ 3.2.1’) //这段仅仅为了方便返回json而已 res.header(“Content-Type”, “application/json;charset=utf-8”); if(req.method == ‘OPTIONS’) { //让options请求快速返回 res.sendStatus(200); } else { next(); } });

JAVA后台配置

JAVA后台配置只需要遵循如下步骤即可:

  • 第一步:获取依赖jar包下载 cors-filter-1.7.jar, java-property-utils-1.9.jar 这两个库文件放到lib目录下。(放到对应项目的webcontent/WEB-INF/lib/下)
  • 第二步:如果项目用了Maven构建的,请添加如下依赖到pom.xml中:(非maven请忽视)

<dependency> <groupId>com.thetransactioncompany</groupId> <artifactId>cors-filter</artifactId> <version>[ version ]</version> </dependency>

其中版本应该是最新的稳定版本,CORS过滤器

  • 第三步:添加CORS配置到项目的Web.xml中( App/WEB-INF/web.xml)

<!– 跨域配置–> <filter> <!– The CORS filter with parameters –> <filter-name>CORS</filter-name> <filter-class>com.thetransactioncompany.cors.CORSFilter</filter-class> <!– Note: All parameters are options, if omitted the CORS Filter will fall back to the respective default values. –> <init-param> <param-name>cors.allowGenericHttpRequests</param-name> <param-value>true</param-value> </init-param> <init-param> <param-name>cors.allowOrigin</param-name> <param-value>*</param-value> </init-param> <init-param> <param-name>cors.allowSubdomains</param-name> <param-value>false</param-value> </init-param> <init-param> <param-name>cors.supportedMethods</param-name> <param-value>GET, HEAD, POST, OPTIONS</param-value> </init-param> <init-param> <param-name>cors.supportedHeaders</param-name> <param-value>Accept, Origin, X-Requested-With, Content-Type, Last-Modified</param-value> </init-param> <init-param> <param-name>cors.exposedHeaders</param-name> <!–这里可以添加一些自己的暴露Headers –> <param-value>X-Test-1, X-Test-2</param-value> </init-param> <init-param> <param-name>cors.supportsCredentials</param-name> <param-value>true</param-value> </init-param> <init-param> <param-name>cors.maxAge</param-name> <param-value>3600</param-value> </init-param> </filter> <filter-mapping> <!– CORS Filter mapping –> <filter-name>CORS</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>

请注意,以上配置文件请放到web.xml的前面,作为第一个filter存在(可以有多个filter的)

  • 第四步:可能的安全模块配置错误(注意,某些框架中-譬如公司私人框架,有安全模块的,有时候这些安全模块配置会影响跨域配置,这时候可以先尝试关闭它们)

JAVA Spring Boot配置

20171230补充

仅列举简单的全局配置

@Configuration public class CorsConfig { private CorsConfiguration buildConfig() { CorsConfiguration corsConfiguration = new CorsConfiguration(); // 可以自行筛选 corsConfiguration.addAllowedOrigin(“*”); corsConfiguration.addAllowedHeader(“*”); corsConfiguration.addAllowedMethod(“*”); return corsConfiguration; } @Bean public CorsFilter corsFilter() { UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration(“/**”, buildConfig()); return new CorsFilter(source); } }

新建配置,然后添加Configuration注解即可配置成功

PS:这一部分方法是收录的,没有亲身实践过,但根据反馈,理论上可行

NET后台配置

.NET后台配置可以参考如下步骤:

  • 第一步:网站配置

打开控制面板,选择管理工具,选择iis;右键单击自己的网站,选择浏览;打开网站所在目录,用记事本打开web.config文件添加下述配置信息,重启网站

前后端分离项目,如何解决跨域问题?

请注意,以上截图较老,如果配置仍然出问题,可以考虑增加更多的headers允许,比如:

“Access-Control-Allow-Headers”:”X-Requested-With,Content-Type,Accept,Origin”

  • 第二步:其它更多配置,如果第一步进行了后,仍然有跨域问题,可能是:接口中有限制死一些请求类型(比如写死了POST等),这时候请去除限 制接口中,重复配置了Origin:*,请去除即可IIS服务器中,重复配置了Origin:*,请去除即可

代理请求方式解决接口跨域问题

注意,由于接口代理是有代价的,所以这个仅是开发过程中进行的。

与前面的方法不同,前面CORS是后端解决,而这个主要是前端对接口进行代理,也就是:

  • 前端ajax请求的是本地接口
  • 本地接口接收到请求后向实际的接口请求数据,然后再将信息返回给前端
  • 一般用node.js即可代理

关于如何实现代理,这里就不重点描述了,方法和多,也不难,基本都是基于node.js的。

搜索关键字node.js,代理请求即可找到一大票的方案。

OPTIONS预检的优化

Access-Control-Max-Age:

这个头部加上后,可以缓存此次请求的秒数。

在这个时间范围内,所有同类型的请求都将不再发送预检请求而是直接使用此次返回的头作为判断依据。

非常有用,可以大幅优化请求次数

如何分析ajax跨域

上述已经介绍了跨域的原理以及如何解决,但实际过程中,发现仍然有很多人对照着类似的文档无法解决跨域问题,主要体现在,前端人员不知道什么时候是跨域问题造成的,什么时候不是,因此这里稍微介绍下如何分析一个请求是否跨域:

抓包请求数据

第一步当然是得知道我们的ajax请求发送了什么数据,接收了什么,做到这一步并不难,也不需要fiddler等工具,仅基于Chrome即可

  • Chrome浏览器打开对应发生ajax的页面,F12打开Dev Tools
  • 发送ajax请求
  • 右侧面板->NetWork->XHR,然后找到刚才的ajax请求,点进去

示例一(正常的ajax请求)

前后端分离项目,如何解决跨域问题?

上述请求是一个正确的请求,为了方便,我把每一个头域的意思都表明了,我们可以清晰的看到,接口返回的响应头域中,包括了

Access-Control-Allow-Headers: X-Requested-With,Content-Type,Accept Access-Control-Allow-Methods: Get,Post,Put,OPTIONS Access-Control-Allow-Origin: *

所以浏览器接收到响应时,判断的是正确的请求,自然不会报错,成功的拿到了响应数据。

示例二(跨域错误的ajax请求)

为了方便,我们仍然拿上面的错误表现示例举例。

前后端分离项目,如何解决跨域问题?

这个请求中,接口Allow里面没有包括OPTIONS,所以请求出现了跨域、

前后端分离项目,如何解决跨域问题?

这个请求中,Access-Control-Allow-Origin: *出现了两次,导致了跨域配置没有正确配置,出现了错误。

更多跨域错误基本都是类似的,就是以上三样没有满足(Headers,Allow,Origin),这里不再一一赘述。

示例三(与跨域无关的ajax请求)

当然,也并不是所有的ajax请求错误都与跨域有关,所以请不要混淆,比如以下:

前后端分离项目,如何解决跨域问题?前后端分离项目,如何解决跨域问题?

比如这个请求,它的跨域配置没有一点问题,它出错仅仅是因为request的Accept和response的Content-Type不匹配而已。

更多

基本上都是这样去分析一个ajax请求,通过Chrome就可以知道了发送了什么数据,收到了什么数据,然后再一一比对就知道问题何在了。

前后端分离项目,如何解决跨域问题?

首先说一下,跨域它不是一个问题,它是浏览器的一种安全策略,叫同源策略。拿前后端分离项目来说,前端调试或者部署的地址,与api的地址,必须拥有相同的协议、域名、端口号。否则,前端请求后端的接口会报错。

如何实现跨域呢?

很多文章都有介绍,使用jsonp、cors服务端配置、代理等等,jsonp这种黑科技还是尽量不用吧,有很大的局限性,并且前后端改动都比较麻烦。

从项目实践的角度来讲,前后端分离项目应该这样实现跨域:

开发阶段,从我带过的项目发现,很多时候前端会要求后端配置cors请求头,然后后端无脑的设置Access-Control-Allow-Origin: *,上线的时候带来很大的安全隐患。正确的姿势应该是前端来解决,前端小白可能惊呆了,咋解决啊?前端框架vue、react都提供了代理功能,配置proxy选项就好了,具体怎么配置前端自己去查查,框架的官方文档都有介绍。

正式上线,这个时候通常使用代理,前端请求接口地址一律使用当前域名下的/api/xxx/xxx。然后配置web服务器,对/api/开头的请求进行代理,转发到后端服务器或者后端部署端口。

如果您有更好的想法,欢迎讨论!

这是个老生常谈的话题,我分情况推荐解决方案。不讲原理,只说方案,干货!请收藏。

  • 如果独立项目,非前后端分离,并且不涉及调用第三方API,则不存在此问题,请直接划过。
  • 前后端分离项目,自己没有后端,直接调用第三方的api,有两种途径解决:1.和第三方服务器开发人员沟通,是否可以开启跨域资源共享(CORS),如果可以,问题已经解决。2.第三方api以安全等为由,不同意开启CORS,或无法联系第三方。则前端可以通过iis,nginx,或者webpack dev server(node.js后端脚本)配置反向代理,变“跨域为同域“。
  • 前后端分离项目,前端调用除自己后端以外的第三方api出现跨域问题。通过上述第二种方案无法解决的,可以和自己项目的后端协商,是否可以让后台转发请求——即让后端去请求响应给前端。(跨域问题仅出现在浏览器脚本中,后端调用不存在此问题)
  • 在已经开启了cors的接口调用时,有时仍然报跨域错误,不一定是跨域问题。请求头或者请求参数不正确,后端没有正常处理,chrome也会报跨域错误。此类问题属于后端bug,后端重点排查是否正确响应了OPTIONS请求。
  • 至于jsonp这种古老的解决方案,在现代开发方式中并不推荐,在以上所有方法均无法解决的的情况下去考虑,但我本人仍然不推荐。

前后端分离项目,如何解决跨域问题?

说到跨域问题,先要说url的组成,url是由协议 + 域名(子域名 + 主域名) + 端口号 + 资源地址组成的。除资源地址外任何一处修改都可认定为跨域。

跨域问题是浏览器和服务器共同作用的安全策略,需要浏览器和服务器双方一起携手才能起到作用。交互过程见下图:

前后端分离项目,如何解决跨域问题?

浏览器跨域交互过程

哪些情况可以被认定为跨域呢?

1、域名不一致,打开页面是a.com下的,跨域到b.com下请求数据。

2、端口不一致,打开页面是a.com下80端口的,跨域到b.com下90端口请求数据。

3、协议不一致,打开页面是http://a.com,跨域到ws://a.com下请求数据。

4、XHR请求,ajax的XHR请求也会被认定为跨域。

解决思路:

1、更改服务器设置,不检查是否跨域,直接放行。

2、更改代理服务器的设置,进行跨域的反向代理。

3、使用jsonp进行ajax的发送,这样就不会产生跨域问题。

php程序处理方式是在代码中添加如下语句:

header(“”Access-Control-Request-Methods:GET, POST, PUT, DELETE, OPTIONS”“);

header(“’Access-Control-Allow-Headers:x-requested-with,content-type,test-token,test-sessid’“);

java处理上这里举例spring boot中的一种方式,就是在controller中类上添加@CrossOrigin注释。样例代码如下:

@RequestMapping(“/test”)

@RestController

@CrossOrigin

public class CorsController {

@GetMapping(“/test”)

public String test() {

return “test”;

}

}

何在服务器上部署一个前后端分离的项目并解决跨域问题

以后端为spring-boot与前台为vue的项目举例。

一.给服务器安装相应的需求环境。

1.安装jdk。

安装方式可自行百度,推荐使用yum安装,安装完成后配置环境即可。

2.安装nginx。代理服务器,安装方式可自行百度,yum应该也是可以安装的。

二.部署后台代码。

1.这里采用IDEA作为开发工具,只要通过maven的打包操作package在target那里找到jar包即可。

前后端分离项目,如何解决跨域问题?

点击package

前后端分离项目,如何解决跨域问题?

复制那个jar包。

2.将jar包复制到服务器上任意目录。

3.打开服务器控制终端,cd到jar包所在目录。

运行java -jar XXXXXX.jar 即可。

当然这样是没法进行另外的操作除非退出来ctrl+C,可以加个停止符号。

java -jar XXXXXX.jar &即可。

三.运行sql文件。

在服务器上安装并启动mysql服务,具体可自行百度。

然后进入mysql,将项目的sql文件放入服务器并运行即可。

需要注意的是。mysql在linux下和windows下有所区别,它的数据库名是区分大小写的,记住你的数据库名要和你后台代码中alidruid的jdbc配置要完全相同。

四.部署前端项目。

1.先进入本地的前端项目,打开config/index.js

配置assetsPublicPath这个属性值为‘/’注意这个属性值要出现两次,需要更改两次。

同时记住dev配置下的proxyTable属性,即后端接口端口的别名,我这里是/api/

前后端分离项目,如何解决跨域问题?

2.这里是webpack打包的,打开控制台。

cd到本地的项目目录下。

运行npm run build。

在本地前端目录下可以看到多了一个dist文件夹,复制它。

3.把dist文件夹复制到服务器上的任意目录。

前后端分离项目,如何解决跨域问题?

4.配置nginx配置使其指向你前端的端口。

A.cd进nginx的配置文件处

前后端分离项目,如何解决跨域问题?

将nginx.conf和nginx.conf.default两个文件内的配置内容给注释了,即每行前面加#,使用vim。

然后在conf.d下面新建一个配置文件。

前后端分离项目,如何解决跨域问题?

如图所示,vi这个文件。

前后端分离项目,如何解决跨域问题?

输入如图所示的文件配置

前后端分离项目,如何解决跨域问题?前后端分离项目,如何解决跨域问题?

其中要注意的是。listen后面跟上你所部署的前端端口。

location后面配置上你所部署的前端静态文件后面所在的具体目录注意root 后面的属性一定要是dist的绝对路径。

后面的一堆addheader可以不加。最后还有个location /api 这里的api要看你后端接口的路径具体别名。proxy_pass要看你后台的端口号。

这里一定要加,不然前端无法调用后台的相应接口。

这里是在用nginx进行反向代理解决跨域问题。

五.访问部署好的网站。

将原来本地访问的localhost改成你的服务器的IP地址名即可。

如果出现了404的问题,根本原因肯定是跨域问题,即nginx没有配置好。

如果出现了403的问题,根本原因肯定是config.js的问题或者nginx的location里没有指向正确的dist路径。

————————————————

原文链接:https://blog.csdn.net/TateBrwonJava/java/article/details/80373913

原创文章,作者:秒收,如若转载,请注明出处:https://www.miaoshou.cc/5204.html

(0)
上一篇 2021年6月24日 15:08
下一篇 2021年6月24日 15:17

相关推荐

发表回复

您的电子邮箱地址不会被公开。