一、跨域问题
1、浏览器控制台出现以下错误,说明是ajax跨域问题
No 'Access-Control-Allow-Origin' header is present on the requested resource.
二、跨域的定义以及产生原因
● 什么是跨域?
说到跨域,就必须知道同源策略,它是一种安全策略,所谓的同源是指域名、协议、端口号 相同。
一个域名的网页去请求另一个域名的资源,即发出去的请求不是本域的请求,不符合同源策略,就造成了
跨域。
● 产生跨域的原因
1、浏览器的限制:
浏览器出于安全考虑,当发现请求时跨域的,就会进行校验,如果校验不通过,就会出现跨域
安全问题
2、请求是跨域的:
发出去的请求不是本域的请求,请求里面协议、域名、端口任何一个不一样,浏览器就认为
是跨域的
3、请求的类型是XHR(XMLHttpRequest)请求:
发出去的请求的必须是XHR类型(type),如果不是这个类型,则浏览器不会报跨域错误
★ 总结:产生跨域的原因有3个,这三个原因必须同时满足,才会发生浏览器跨域问题
三、解决思路
1、解决浏览器的限制:
可以通过浏览器指定参数,让浏览器不去做这个校验,这个跨域问题就解决了,但是这种方法价值不大,因为需要每个人都在客户端进行改动
2、改变XHR请求类型
只要发出去的请求类型不是XHR类型的,就算是跨域的,浏览器控制台也不会报跨域安全问题
★ 基于这种思路,我们的解决方案是 :JSONP,它通过动态创建Script,在Script中发出跨域请求,
但是JSONP的解决方案有很多弊端,无法满足现在开发的要求,所以现在使用的人越来越少了
3、跨域
所以解决跨域问题,重点放在怎么解决跨域,两种思路:
①、被调用方支持跨域
比如:A域名 调用 B域名,只要给B域名返回的信息中加入一些字段,告诉浏览器我允许A域名的调用,只要浏览器通过校验,
就不会出现跨域问题
②、调用方隐藏跨域
通过一个代理,从浏览器发出去的都是A域名的请求,在代理里面把指定的URL转到B域名中,这样在浏览器看来就是同
一个域名,就没有跨域问题,所以跨域问题就解决了
四、JSONP
1、JSONP(JSON with Padding)是JSON的一种“使用模式”,可用于解决主流浏览器的跨域数据访问的问题。
2、使用JSONP,发送的请求类型是script;返回的类型是js脚本(javaScript);JSONP的URL后面加了一个callback参数,
服务器把返回的内容由JSON变为JavaScript,JavaScript的内容就是一个函数调用
3、JSONP的缺点:
①、它只支持GET请求而不支持POST等其它类型的HTTP请求;
②、它发送的请求类型不是XHR类型
③、服务器需要改动代码支持
4、实现原理
JSONP是一个非官方协议,是一个前后台的约定,约定发送请求的参数中如果包含指定的参数,
默认为callback,那么就是一个JSONP请求, 服务器发现是一个JSONP请求,就把返回的数据由
原来的JSON对象改为一个JS函数调用的形式,callback的值作为函数名,原来返回的JSON对象作
为函数的参数返回。
JavaEE架构.png
五、跨域解决的方向
▲ 普通请求和跨域请求的区别是:跨域请求的请求头中会有一个Origin字段,这个字段的值是当前域名的信息,当浏览器发现这个请求是跨域的时候,
就会在请求头添加一个Origin字段,然后等请求返回的时候,就会检查响应头中是否有允许跨域的信息,如果没有,就会报错。
1、被调用方解决
被调用方解决支持跨域,主要是添加响应头字段,告诉浏览器支持跨域调用,有三种方法:
①、服务端实现(重点)
简单请求是先执行,后判断,非简单请求是先发一个OPTIONS预检命令,如果检查通过,才会真正把跨域请求发送出去
工作中比较常见的[简单请求]:
Methods: GET, HEAD, POST
请求 header 里面:
* 无自定义头
* Content-Type 为以下几种:
text/plain
multipart/form-data
application/x-www-form-urlencoded
工作中常见的[非简单请求]:
* put, Delete 方法的 ajax 请求
* 发送JSON格式的 ajax 请求
* 带自定义头的 ajax 请求
● 被调用方解决方案:filter解决
创建一个filter,在filter的doFilter方法中添加如下响应头字段:
response.addHeader("Access-Control-Allow-Origin", "*"); //允许这个域跨域调用
response.addHeader("Access-Control-Allow-Methods", "*"); //指定允许的方法
response.addHeader("Access-Control-Allow-Headers", "Content-Type"); //告诉浏览器允许这个Header
response.addHeader("Access-Control-Max-Age", "3600"); //告诉浏览器允许在指定时间(秒)缓冲信息,不需要再发送预检命令
▲ 带Cookie的跨域:
(1)、Cookie要加再被调用方;
(2)、浏览器发现是跨域的时候,会在请求头中添加Oragin字段,该字段中包含了当前域的信息
(3)、要想支持所有域的跨域请求,就需要在request中取出Oragin字段,然后判断该字段不为空时,把该字段设置到
response.addHeader("Access-Control-Allow-Origin", oragin); //调用方的URL
在Filter中添加如下,Origin参数为 * 时,是不能满足带Cookie的跨域请求的,必须全匹配
response.addHeader("Access-Control-Allow-Origin", "http://localhost:8081"); //调用方的URL
response.addHeader("Access-Control-Allow-Credentials", "true"); //允许Cookie
Ajax携带Cookie.png
▲ 带自定义头的跨域:
添加自定义头的两种方式.png
支持所有自定义头.png
②、NGINX配置 【重点】
● 被调用方解决方案:Nginx解决
③、Spring框架解决方案 【重点】
只需要添加 @CrossOrigin注解,可以加在类上面(表示这个类上的所有方法都支持跨域),也可以加在方法上面
④、APACHE配置
2、调用方解决方案(隐藏跨域)
跨域请求通过调用方的Http服务器的反向代理转发到被调用方的服务器,在浏览器看来,都是同一域名
①、NGINX配置(反向代理) 【重点】
● 调用方解决方案:Nginx解决
②、APACHE配置
● 调用方解决方案:APACHE解决
隐藏跨域和支持跨域最大的不同是,隐藏跨域调用的URL都是本域的,使用的是相对地址,而支持跨域使用的是绝对地址
五、浏览器禁止检查
命令行参数启动浏览器,输入:
chrome --disable-web-security --user-data-dir=g:\temp3
网友评论