icon-cookie
The website uses cookies to optimize your user experience. Using this website grants us the permission to collect certain information essential to the provision of our services to you, but you may change the cookie settings within your browser any time you wish. Learn more
I agree
blank_error__heading
blank_error__body
Text direction?

前后端分离项目,如何解决跨域问题?-java架构设计的回答-悟空问答

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

前后端分离项目跨域问题是不可避免的。通常情况下前端由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项目都去编写跨域的代码。

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

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

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

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

什么是跨域

跨域(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)》

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

展开全部

跨域资源共享(CORS)是前后端分离项目很常见的问题,本文主要介绍当SpringBoot应用整合SpringSecurity以后如何解决该问题。

什么是跨域问题

CORS全称Cross-Origin Resource Sharing,意为跨域资源共享。当一个资源去访问另一个不同域名或者同域名不同端口的资源时,就会发出跨域请求。如果此时另一个资源不允许其进行跨域资源访问,那么访问的那个资源就会遇到跨域问题。

跨域问题演示及解决

我们使用mall项目的源代码来演示一下跨域问题。此时前端代码运行在8090端口上,后端代码运行在8080端口上,由于其域名都是localhost,但是前端和后端运行端口不一致,此时前端访问后端接口时,就会产生跨域问题。

点击前端登录按钮

此时发现调用登录接口时出现跨域问题。

覆盖默认的CorsFilter来解决该问题

添加GlobalCorsConfig配置文件来允许跨域访问。

package com.macro.mall.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import org.springframework.web.filter.CorsFilter; /** * 全局跨域配置 * Created by macro on 2019/7/27. */@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); }}

重新运行代码,点击登录按钮

发现需要登录认证的/admin/info接口的OPTIONS请求无法通过认证,那是因为复杂的跨越请求需要先进行一次OPTIONS请求进行预检,我们的应用整合了SpringSecurity,对OPTIONS请求并没有放开登录认证。

设置SpringSecurity允许OPTIONS请求访问

在SecurityConfig类的configure(HttpSecurity httpSecurity)方法中添加如下代码。

.antMatchers(HttpMethod.OPTIONS)//跨域请求会先进行一次options请求 .permitAll()

重新运行代码,点击登录按钮

发现已经可以正常访问。

一次完整的跨域请求

先发起一次OPTIONS请求进行预检

  • 请求头信息:

Access-Control-Request-Headers: content-type Access-Control-Request-Method: POST Origin: http://localhost:8090 Referer: http://localhost:8090/ User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36

  • 响应头信息:

Access-Control-Allow-Credentials: true Access-Control-Allow-Headers: content-type Access-Control-Allow-Methods: POST Access-Control-Allow-Origin: http://localhost:8090 Cache-Control: no-cache, no-store, max-age=0, must-revalidate Content-Length: 0 Date: Sat, 27 Jul 2019 13:40:32 GMT Expires: 0 Pragma: no-cache Vary: Origin, Access-Control-Request-Method, Access-Control-Request-Headers X-Content-Type-Options: nosniff X-Frame-Options: DENY X-XSS-Protection: 1; mode=block

  • 请求成功返回状态码为200

发起真实的跨域请求

  • 请求头信息:

Accept: application/json, text/plain, */* Content-Type: application/json;charset=UTF-8 Origin: http://localhost:8090 Referer: http://localhost:8090/ User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36 {username: "admin", password: "123456"} password: "123456" username: "admin"

  • 响应头信息:

Access-Control-Allow-Credentials: true Access-Control-Allow-Origin: http://localhost:8090 Cache-Control: no-cache, no-store, max-age=0, must-revalidate Content-Type: application/json;charset=UTF-8 Date: Sat, 27 Jul 2019 13:40:32 GMT Expires: 0 Pragma: no-cache Transfer-Encoding: chunked Vary: Origin, Access-Control-Request-Method, Access-Control-Request-Headers X-Content-Type-Options: nosniff X-Frame-Options: DENY X-XSS-Protection: 1; mode=block

  • 请求成功返回状态码为200
展开全部
Measure
Measure
Related Notes
Get a free MyMarkup account to save this article and view it later on any device.
Create account

End User License Agreement

Summary | 13 Annotations
跨域资源共享
2020/06/10 11:55
禁止使用XHR对象向不同源的服务器发起http请求
2020/06/10 11:55
HEAD、GET、POST三种方法之一
2020/06/10 11:57
OPTIONS请求
2020/06/10 11:57
非简单请求
2020/06/10 11:57
204或200状态码
2020/06/10 11:57
再继续发送对应的GET/POST/PUT/DELETE请求
2020/06/10 11:57
nginx配置
2020/06/10 11:58
数据中心化
2020/06/10 11:58
微服务概
2020/06/10 11:59
面对各种端
2020/06/10 11:59
同源是指,域名,协议,端口均为相同
2020/06/10 12:00
CORS是一个W3C标准,全称是“跨域资源共享”(跨源资源共享)
2020/06/10 12:00