java、ajax 跨域请求解决方案('Access-Control-Allow-Origin' header is present on the requested resource. Origin '请求源' is therefore not allowed access.)
1.情景展示
ajax调取java服务器请求报错
报错信息如下:
'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'null' is therefore not allowed access.
但是,请求状态却是成功:200,这是怎么回事?
2.原因分析
ajax请求跨域:ajax出现请求跨域错误问题是因为浏览器的“同源策略”。
同源策略:1995年,同源政策由 Netscape 公司引入浏览器。目前,所有浏览器都实行这个政策。
所谓"同源"指的是"三个相同":协议相同&域名相同&端口相同,这三个要求必须一致,否则就叫跨域。
同源政策的目的,是为了保证用户信息的安全,防止恶意的网站窃取数据(比如:同源策略可以限制不同网站cookie共享的问题,通过cors设置照样可以实现cookie共享)。
同源政策规定,AJAX请求只能发给同源的网址,否则就报错。
3.ajax解决方案
方法一:JSONP
简述:网页通过添加一个<script>元素,向服务器请求JSON数据,这种做法不受同源政策限制;服务器收到请求后,将数据放在一个指定名字的回调函数里传回来。
但是,只能发送get请求,比较鸡肋,其优势在于支持老式浏览器,以及可以向不支持CORS的网站请求数据。
方法二:WebSocket
WebSocket是一种通信协议,使用ws://(非加密)和wss://(加密)作为协议前缀。该协议不实行同源政策,只要服务器支持,就可以通过它进行跨源通信。
方法三:CORS(推荐使用)
CORS是跨源资源分享(Cross-Origin Resource Sharing)的缩写。它是W3C标准,是跨源AJAX请求的根本解决方法。相比JSONP只能发GET请求,CORS支持所有类型的HTTP请求。
它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。
CORS需要浏览器和服务器同时支持。目前,所有浏览器都支持该功能,IE浏览器不能低于IE10。
整个CORS通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS通信与同源的AJAX通信没有差别,代码完全一样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。
因此,实现CORS通信的关键是服务器,只要服务器实现了CORS接口,就可以跨源通信。
java服务器设置
第一步:jar包配置
所需jar包:cors-filter-2.8.jar和java-property-utils-1.9.1.jar(这两个库文件放到对应项目的WEB-INF/lib/下)
如果是maven项目,将如下代码添加到pom.xml中
<dependency>
<groupId>com.thetransactioncompany</groupId>
<artifactId>cors-filter</artifactId>
<version>[ version CORS过滤器的最新的稳定版本 ]</version>
</dependency>
第二步:添加CORS配置到项目的web.xml中( App/WEB-INF/web.xml)
<!-- 跨域配置CORS-->
<filter>
<!-- The CORS filter with parameters -->
<filter-name>CORSFilter</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. -->
<!-- 是否允许http请求 -->
<init-param>
<param-name>cors.allowGenericHttpRequests</param-name>
<param-value>true</param-value>
</init-param>
<!--
允许跨域的域名(发送请求至该项目的地址、请求源)
构成(http://域名:端口号),比如(http://192.168.191.115:8080)
-->
<init-param>
<param-name>cors.allowOrigin</param-name>
<!--
*,表示:允许所有跨域请求,这样的后果是:当需要客户端请求携带cookie时,浏览器无法携带cookie至服务器
可以在servlet中动态设置:response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin"));
这,同样是允许所有跨域请求,servlet的设置会覆盖该属性设置。
但是,当实际测试后发现,这里的*永远指向的是请求头的Origin的值,所以不需要再进行额外的设置。
-->
<param-value>*</param-value>
</init-param>
<!-- 允许子域 -->
<init-param>
<param-name>cors.allowSubdomains</param-name>
<param-value>false</param-value>
</init-param>
<!-- 允许的请求方式(非简单请求必须添加OPTIONS,因为"预检"请求用的请求方法是OPTIONS,表示这个请求是用来询问的。) -->
<init-param>
<param-name>cors.supportedMethods</param-name>
<param-value>GET, HEAD, POST, OPTIONS,PUT</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>
<!--
自定义暴露自己的请求头(自定义设置后Response Headers里会显示Access-Control-Expose-Headers及值)
CORS请求时,XMLHttpRequest对象的getResponseHeader()方法只能拿到6个基本字段:Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma;
如果想拿到其他字段,就必须在Access-Control-Expose-Headers里面指定。
-->
<init-param>
<param-name>cors.exposedHeaders</param-name>
<!--这里可以添加一些自己的暴露Headers -->
<param-value>X-Test-1, X-Test-2</param-value>
</init-param>
<!-- 允许客户端给服务器发送cookie,如果不允许,删除该属性即可。(携带证书访问) -->
<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>
<!-- CORS Filter mapping -->
<filter-mapping>
<filter-name>CORSFilter</filter-name>
<!-- 可自定义设置可供访问的项目路径 -->
<url-pattern>/*</url-pattern>
</filter-mapping>
注意:当web.xml文件中配置了多个filter时,需要将以上配置放在最前面,即:作为第一个filter存在。
4.效果展示
ajax代码(html需要引入jQuery)
servlet处理
请求完成
没有添加cors配置前
http的请求头
ajax的请求头
经过对比发现,我们会发现两点不同:
http请求不存在跨域问题,ajax出现跨域会报错(跨域提醒且无法实现数据交互),输出在浏览器的控制台上;
ajax请求会在Request Headers请求头会增加一个头部属性:Origin,值为当前网页地址,形如:http://localhost:8070,但是,当浏览器检测出该AJAX请求是跨域请求时,它的值会设置为null。
Origin
字段用来说明:本次请求来自哪个源(协议 + 域名 + 端口)。服务器根据这个值,决定是否同意这次请求。
如果Origin
指定的源,不在许可范围内,服务器会返回一个正常的HTTP回应。浏览器发现,这个回应的头信息没有包含Access-Control-Allow-Origin
字段(详见),就知道出错了,从而抛出一个错误,被XMLHttpRequest
的onerror
回调函数捕获。
注意:这种错误无法通过状态码识别,因为HTTP回应的状态码有可能是200(事实上,咱们本次的状态码就是200)。
添加cors配置后
ajax可以实现跨域请求,即:Origin
指定的域名在许可范围内,服务器返回的响应,会多出几个头信息字段。
Response Headers会多出以下字段:
Origin
字段的值,要么是一个*
,表示接受任意域名的请求。(必返回项) 注意:如果是非简单请求,必返回的字段是Access-Control-Request-Method,。
小结:
第一,单纯的http请求不存在跨域问题,ajax请求存在跨域问题。(而也是使用java发送http请求至服务器不存在跨域问题的根本原因)
第二,跨域请求,请求头会自动加上Origin字段;当服务器添加cors配置后,响应头必返回Access-Control-Allow-Origin字段;
如果是非简单请求可能会返回:Access-Control-Request-Method,表示:服务器端允许接受的http请求方式(当请求数据格式指定为application/json时,并没有出现该字段,按理说该出来的)。
4.如何复现跨域问题?
我们现在知道:协议、域名、端口三者只要有一个不同,就叫跨域。那么,要重现跨域场景,就需要两台服务器,即同一台电脑,跑两个tomcat服务器即可(两个tomcat的端口号必须不同,否则端口冲突)。
这里只介绍最简单的一种跨域请求方式:
将ajax代码写到一个单独的html文件中,运行你的tomcat服务器, 使用浏览器打开该网页文件,就可重现ajax跨域问题啦。
一旦服务器通过了"预检"请求,以后每次浏览器正常的CORS请求,就都跟简单请求一样,会有一个Origin
头信息字段。服务器的回应,也都会有一个Access-Control-Allow-Origin
头信息字段。
如上图所示,重现跨域问题后,按照第三步的cors方案,就能解决跨域问题啦。
5.浏览器如何向服务器跨域传送cookie?
错误示例:
本机运行了两个项目对应两个tomcat,端口号分别是8080和8070,
如上图所示,Host代表的是服务端,Origin代表的是客户端,两者的域名不同,其结果就是:
虽然可以实现跨域,但是,无法实现cookie共享(服务端照样可以返回cookie且浏览器能接收到,但是,浏览器发送请求至服务器时却无法携带cookie)。
由此可见,当域名完全不一致时,cookie无法实现跨域(子域不同的情况我没有进行测试)。
这让我误以为,cookie跨域共享是无法实现的,但是,事实并非如此。
正确示例:
localhost:8080的服务器也能接收到实际的数据
打印结果
具体实现:
window.onload = function(){
// 前端往项目当中添加cookie
document.cookie = "name=Marydon;path=/";
$.ajax({
url:'http://localhost:8080/test/crossServlet',
method:'post',
xhrFields: {
withCredentials: true
},// cookie能够传过去的关键所在
/*crossDomain: true,*/
data:{name:'张三'},
success:function(data) {
alert(data);
}
});
}
前端设置:关键就在于,ajax需要添加参数:xhrFields:{withCredentials:true};
后台设置:(添加响应头部信息设置,response.setHeader())
配置Access-Control-Allow-Credentials,值为true,对应cors的cors.supportsCredentials;
配置Access-Control-Allow-Origin,值为不能为 '*',对应cors的cors.allowOrigin,但经过实践发现,其值为*时并没有产生影响。
说明:
在前端添加cookie时,必须设置路径,不然,cookie只作用于当前页面;
在默认情况下,只有设置 cookie的网页才能读取该 cookie。如果想让一个页面读取另一个页面设置的cookie,必须设置cookie的路径。
cookie的路径用于设置能够读取 cookie的顶级目录。将这个路径设置为网站的根目录(/),这样所有网页都能读取到该cookie,
这也是js设置cookie后,后台取不到值的原因。
另外,在前端是获取不到JSESSIONID这个cookie的,因为它设置了httpOnly属性,即:只有后台能够得到该cookie。
小结:
cookie的跨域共享不是无条件的,即:请求和响应的IP完全不相同时,无法实现cookie共享,这就相当于A网站不能访问B网站的cookie一个道理。
当请求发起方和接收方的域名(IP)完全一致,端口号不同时,浏览器是可以携带cookie的,也就是:服务器能接收到前端所传来的cookie。
当IP的顶级域名相同时,没有进行测试。
经过上述实践发现:跨域cookie共享的局限性很大,还不如不用,有实际使用场景的大佬,欢迎留言。
6.http请求Headers详细说明
java、ajax 跨域请求解决方案('Access-Control-Allow-Origin' header is present on the requested resource. Origin '请求源' is therefore not allowed access.)的更多相关文章
- 跨域问题解决----NO 'Access-Control-Allow-Origin' header is present on the requested resource.Origin'http://localhost:11000' is therfore not allowed access'
NO 'Access-Control-Allow-Origin' header is present on the requested resource.Origin'http://localhost ...
- WCF REST开启Cors 解决 No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost' is therefore not allowed access. The response had HTTP status code 405.
现象: 编写了REST接口: [ServiceContract] public interface IService1 { [OperationContract] [WebInvoke(UriTemp ...
- Failed to load http://wantTOgo.com/get_sts_token/: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://fromHere.com' is therefore not allowed access.
Failed to load http://wantTOgo.com/get_sts_token/: No 'Access-Control-Allow-Origin' header is presen ...
- XMLHttpRequest cannot load ''. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin ' ' is therefore not allowed access.
ajax跨域 禁止访问! 利用Access-Control-Allow-Origin响应头解决跨域请求
- No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'null' is therefore not allowed access.
一.什么是跨域访问 举个栗子:在A网站中,我们希望使用Ajax来获得B网站中的特定内容.如果A网站与B网站不在同一个域中,那么就出现了跨域访问问题.你可以理解为两个域名之间不能跨过域名来发送请求或者请 ...
- (转)AJax跨域:No 'Access-Control-Allow-Origin' header is present on the requested resource
在本地用ajax跨域访问请求时报错: No 'Access-Control-Allow-Origin' header is present on the requested resource. Ori ...
- PHP Ajax 跨域问题解决方案
本文通过设置Access-Control-Allow-Origin来实现跨域. 例如:客户端的域名是client.0751.tv,而请求的域名是server.0751.tv. 如果直接使用ajax访问 ...
- ajax跨域(No 'Access-Control-Allow-Origin' header is present on the requested resource)
问题 在某域名下使用Ajax向另一个域名下的页面请求数据,会遇到跨域问题.另一个域名必须在response中添加 Access-Control-Allow-Origin 的header,才能让前者成功 ...
- js跨域访问,No 'Access-Control-Allow-Origin' header is present on the requested resource
js跨域访问提示错误:XMLHttpRequest cannot load http://...... No 'Access-Control-Allow-Origin' header is prese ...
随机推荐
- Docker Hub镜像加速器
国内从 Docker Hub 拉取镜像有时会遇到困难,此时可以配置镜像加速器.Docker 官方和国内很多云服务商都提供了国内加速器服务. 1.配置加速地址 vim /etc/docker/daemo ...
- [IDA] Oops! internal error 40343 occured.
问题描述: 解决方案: 安装 IDA 时,其路径下不要出现中文.
- 搭建 Frp 来远程内网 Windows 和 Linux 机子
魏刘宏 2019 年 5 月 19 日 一.使用一键脚本搭建服务端 Frp 这个内网穿透项目的官方地址为 https://github.com/fatedier/frp ,不过我们今天搭建服务端时不直 ...
- 来点高逼格的,使用前端Sendmessage实现SSO
关于什么叫SSO(单点登录),这个概念具体的信息我就不详述了,网上一搜一大把,总的来说,它是一种可以控制各个独立的系统经过它的授权后, 可以用同一个帐号体系登录不同的系统,达到一处登录,多处使用的效果 ...
- Hystrix核心熔断器
在深入研究熔断器之前,我们需要先看一下Hystrix的几个重要的默认配置,这几个配置在HystrixCommandProperties 中 //时间窗(ms) static final Integer ...
- Vue 拖拽组件 vuedraggable 和 vue-dragging
一.描述 之前用 vue 写过一个在线的多二维码生成服务,体验地址:https://postbird.gitee.io/vue-online-qrcode/ 后面发现二维码多了之后有时候想要排序,需要 ...
- SAP 同一个序列号可以同时出现在2个不同的HU里?
SAP 同一个序列号可以同时出现在2个不同的HU里? 答案是可以的. 如下图示,HU 180141003288里的序列号11810010540121, 而序列号11810010540121已经出现在另 ...
- Qt导航栏 QListWidget
使用Qt Designer 使用QListWidget控件 设置样式 QListWidget::item { min-height: 30px; /*设置item高度*/ border-style: ...
- Java打印9*9乘法表
废话不多说直接贴代码, 先放一个标准的正三角形状的 for (int i = 1; i <= 9; i++) { for (int j = 1; j <= i; j++) { System ...
- 困扰了2天的问题,终于解决了。VB6的MSComCtl.ocx在32位Win7显示对象库未注册
解决方案在这里,中文的资料真的挺垃圾的.(重启几次之后又不行了....怎么回事???) 安装.net framework4.0以上版本, C:\Windows\System32, C:\Windows ...