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字段(详见),就知道出错了,从而抛出一个错误,被XMLHttpRequestonerror回调函数捕获。

  注意:这种错误无法通过状态码识别,因为HTTP回应的状态码有可能是200(事实上,咱们本次的状态码就是200)。

  添加cors配置后

  ajax可以实现跨域请求,即:Origin指定的域名在许可范围内,服务器返回的响应,会多出几个头信息字段。

  Response Headers会多出以下字段:  

  Access-Control-Allow-Credentials:true,表示:允许客户端发送cookie,如果服务器没有配置该字段,则不会返回;(可返回项)
  Access-Control-Allow-Origin:null,表示:允许发送请求的客户端的域名,它的值要么是请求时Origin字段的值,要么是一个*,表示接受任意域名的请求。(必返回项)  
  Access-Control-Expose-Headers:X-Test-2, X-Test-1,表示:客户端可以获取的非简单响应标头或者自定义的响应头对应的值。(可返回项,如果不指定,则浏览器只能从headers中获取:Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma的值)

  注意:如果是非简单请求,必返回的字段是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.)的更多相关文章

  1. 跨域问题解决----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 ...

  2. 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 ...

  3. 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 ...

  4. 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响应头解决跨域请求

  5. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'null' is therefore not allowed access.

    一.什么是跨域访问 举个栗子:在A网站中,我们希望使用Ajax来获得B网站中的特定内容.如果A网站与B网站不在同一个域中,那么就出现了跨域访问问题.你可以理解为两个域名之间不能跨过域名来发送请求或者请 ...

  6. (转)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 ...

  7. PHP Ajax 跨域问题解决方案

    本文通过设置Access-Control-Allow-Origin来实现跨域. 例如:客户端的域名是client.0751.tv,而请求的域名是server.0751.tv. 如果直接使用ajax访问 ...

  8. ajax跨域(No 'Access-Control-Allow-Origin' header is present on the requested resource)

    问题 在某域名下使用Ajax向另一个域名下的页面请求数据,会遇到跨域问题.另一个域名必须在response中添加 Access-Control-Allow-Origin 的header,才能让前者成功 ...

  9. 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 ...

随机推荐

  1. SpringBoot2版本Caused by: java.sql.SQLSyntaxErrorException: Table 'dinner.hibernate_sequenc

    1.SpringBoot2版本Caused by: java.sql.SQLSyntaxErrorException: Table 'dinner.hibernate_sequenc报错. -java ...

  2. 使用角色管理工具 安装或配置microsoft.net framework 3.5 sp1

    解决方法:

  3. WEB网站发布服务器IIS报错问题终极解决方案,查到问题点

    4本次错误webservice发布新服务器后,出现此错误. 解决方法: 找到dmp文件 dmp文件是啥?自己百度.简单的说就是黑匣子,记录程序崩溃前的操作,那么如何找到这个黑匣子呢? 1.启动 Win ...

  4. 使用三层架构+EF添加单元测试

    在运行测试的时候抛异常了: “System.InvalidOperationException”类型的异常在 mscorlib.dll 中发生,但未在用户代码中进行处理 The Entity Fram ...

  5. 实战Rest API接口测试

    一.Android App API接口测试 1.如何学好Android App API接口测试 postman可以用来实现API接口自动化测试,但是也有弊端,无法实现接口测试数据的参数化,为了达到接口 ...

  6. ICSharpCode.SharpZipLib 中文乱码问题

    今天在调用ICSharpCode.SharpZipLib解压zip文件时出现了中文文件乱码的问题. 解决过程如下: 1.判断是否压缩包本身问题.经查zip文件夹在本地直接解压打开时正确的中文名称,所以 ...

  7. NopCommerce源代码分析之用户验证和权限管理

    目录 1.  介绍 2.  UML 2.1  实体类UML图 2.2  业务相关UML图 3.  核心代码分析 3.1  实体类源代码 3.2  业务相关源代码 3.3  相关控制器源代码 3.4  ...

  8. ANDROID培训准备资料之四大组件的简单介绍

    Android四大组件是一个android app 最基本的组成部分,这篇博客主要给大家简单的介绍一下四种组件 (1)Activities (2)Services (3)BroadcastReceiv ...

  9. Python odoo中嵌入html简单的分页功能

    在odoo中,通过iframe嵌入 html,页面数据则通过controllers获取,使用jinja2模板传值渲染 html页面分页内容,这里写了判断逻辑 <!-- 分页 -->< ...

  10. Gradle 创建java程序详细步骤

    Java构建工具三强: Ant, Maven, GradleAnt历史悠久, 用build.xml 描述, 当时他的xml着实让很多工程师头痛, 但仍有用武之地. Maven 用pom.xml 文件描 ...