ajax 跨域问题笔记
产生原因
浏览器限制
跨域
XHR请求
解决思路

解决办法
1. 浏览器限制
浏览器禁止检查(不推荐)
2. 跨域
① 被调用方解决(支持跨域)
a. filter 解决方案(增加请求头方式)
b. Spring 框架注解 @CrosOrigin
c. 修改Nginx配置
d. 修改Apach配置
② 调用方解决(隐藏跨域)
修改Nginx、apache反向代理
3. XHR请求
JOSNP: 需要前后台一起基于约定进行改动,基于动态创建js请求并发送,返回数据也是js脚本
缺点:
① 服务器也需要改动
② 仅支持get方法
③ 发送的不是XHR请求
/* 一.浏览器多管闲事-----------------------------------------------------搞定浏览器: 让浏览器滚,不判断 1.启动浏览器的时候加参数 2.浏览器跨域插件 二.浏览器判断类型: XHR (XMLHttpRequest)-------------------------------发送类型不是XHR, JSONP就是利用这一条. JSONP一个非官方的协议, JSONP的弊端: 1.JSONP需要后台配合, 要有一个参数, 默认是?callback=JQuery1234567... JSONP返回的类型是:javascript, 而非json 2.只可以发送get请求 3.发送的不是XHR请求(这也是它可以跨域的原因),没有回调及事件的特性 三.浏览器判断跨域: 协议, 域名, 端口, 任何一个不一样就是跨域-----------1.服务器告诉浏览器可以跨域. 2.前端通过代理欺骗浏览器是同一个域名 1.后端处理: a)应用服务器(TOMCAT)--Filter, "Access-Control-Allow-Origin": "http://localhost:8081" "Access-Control-Allow-Methods": "GET" "Access-Control-Allow-Headers": "Content-Type, custom-header1, custom-header2" //非简单请求每次会发出去两次请求,预检命令(OPTIONS)和真正的请求 "Access-Control-Max-Age": "3600" //预检命令(OPTIONS)为1个小时, 这样1个小时内就不用发送两次请求 "Access-Control-Allow-Credentials": "true" //不是"Access-Control-Allow-Origin": "*"就无敌了, 带Cookie的请求就行不通! b)HTTP服务器(NGINX) 配置比apache简单些 c)HTTP服务器(APACHE) 相对nginx复杂一些 d)Spring框架解决方案 @CrossOrigin 2.前端处理(隐藏跨域): a)反向代理-NGINX配置 在请求的时候写相对路径就行, 浏览器认为请求是同域的, 不会做跨域判断. 后端处理时, 请求地址都是绝对路径 b)反向代理-APACHE配置 以上一,二,三个条件必需同时满足, 才会生跨域问题! 问答: -> 为什么已经返回200了还报错: 因为是先发送, 浏览器再判断是否跨域. 如果存在跨域条件, 则报错. */
总结
跨域产生的原因:
浏览器限制。如果浏览器发现请求是跨域的时候,就会做校验,如果校验不通过就会报跨域的错误
跨域。发出去的请求只要域名、端口、协议中的任意一个与当前域不同的时候,都会发生跨域
发送的XHR(XMLHTTPRequest)请求。如果发送的不是xhr请求,无论是否跨域,浏览器都不会报错
只有这三种原因同时满足时,才会发生跨域
解决跨域的方法
浏览器角度。让浏览器不做校验,浏览器的设置跨域,在浏览器的属性设置页面的目标输入框加上--disable-web-security。这样浏览器将不会去做校验了。但是每个人都需要去改动,不建议使用。
发送xhr请求角度。让跨域的请求不发送xhr请求,就不会再报错了。办法是使用jsonp:jsonp是非官方协议,是前后端的一种约定,前端使用ajax发送请求,dataType为jsonp,并且带一个参数(默认是callback),当后台发现这个参数之后,就知道这是一个jsonp请求,就会把原本返回json对象编程js返回,js代码是一个函数的执行,函数名是callback的参数值,函数的参数是原本的json对象。缺点:1).只支持get方法请求;2).需要服务器修改代码;3).发送的不是xhr请求
在跨域角度。1).被调用方解决:在响应头增加指定字段,告诉浏览器,允许调用,这种方法的原理是从根源支持跨域的。各种解决方式,请参考详细解决笔记;2).调用方解决:这种解决办法原理是隐藏跨域。使用代理,在同一个域请求不同的url地址,转发到不同的域。
总结
跨域产生的原因:
浏览器限制。如果浏览器发现请求是跨域的时候,就会做校验,如果校验不通过就会报跨域的错误
跨域。发出去的请求只要域名、端口、协议中的任意一个与当前域不同的时候,都会发生跨域
发送的XHR(XMLHTTPRequest)请求。如果发送的不是xhr请求,无论是否跨域,浏览器都不会报错
只有这三种原因同时满足时,才会发生跨域
解决跨域的方法
浏览器角度。让浏览器不做校验,浏览器的设置跨域,在浏览器的属性设置页面的目标输入框加上--disable-web-security。这样浏览器将不会去做校验了。但是每个人都需要去改动,不建议使用。
发送xhr请求角度。让跨域的请求不发送xhr请求,就不会再报错了。办法是使用jsonp:jsonp是非官方协议,是前后端的一种约定,前端使用ajax发送请求,dataType为jsonp,并且带一个参数(默认是callback),当后台发现这个参数之后,就知道这是一个jsonp请求,就会把原本返回json对象编程js返回,js代码是一个函数的执行,函数名是callback的参数值,函数的参数是原本的json对象。缺点:1).只支持get方法请求;2).需要服务器修改代码;3).发送的不是xhr请求
在跨域角度。1).被调用方解决:在响应头增加指定字段,告诉浏览器,允许调用,这种方法的原理是从根源支持跨域的。各种解决方式,请参考详细解决笔记;2).调用方解决:这种解决办法原理是隐藏跨域。使用代理,在同一个域请求不同的url地址,转发到不同的域。
一、解决跨域的思路:
1.被调用方解决-支持跨域(根据http协议关于跨域方面的要求,增加响应头信息,告诉浏览器允许被跨域调用)
(1)使用filter解决
res.addHeader("Access-Control-Allow-Origin", "http://localhost:8081"); //设置允许http://localhost:8081域访问,*表示所有域都能访问
res.addHeader("Access-Control-Allow-Methods", "GET");//设置允许GET方法访问,*表示所有方法
(2)nginx解决方案
(3)APACHE解决方案
(4)Spring解决方案 加@CrossOrigin注解即可
2.调用方使用代理做调用解决跨域问题-隐藏跨域
(1)使用反向代理
反向代理:访问同一个域名的两个url,会去到两个不同的服务器
二、简单请求和非简单请求
简单请求:先执行后判断
工作中常见的简单请求:
方法为:GET,HEAD,POST
请求header里面:无自定义头
Content-Type为以下几种:text/plain,multipart/form-data,application/x-www-form-urlencoded
非简单请求:先发预options预检命令,通过后再发跨域请求
res.addHeader("Access-Control-Allow-Headers", "Content-Type");//允许预检命令
res.addHeader("Access-Control-Max-Age", "3600");//预检命令缓存秒数
工作中常见的非简单请求:put,delete的请求,发送json格式的请求,带自定义头的请求
三、带Cookie的跨域请求,response中需要设置
res.addHeader("Access-Control-Allow-Credentials", "true");
res.addHeader("Access-Control-Allow-Origin", "http://localhost:8081");//带Cookie时Access-Control-Allow-Origin需要全匹配,不能用通配符
当多个域名都需要带Cookie跨域访问时,从Filter里面拿到Origin信息,把这个字段写入到Access-Control-Allow-Origin里面
String origin = req.getHeader("Origin");
if(!StringUtils.isEmpty(origin)) {
res.addHeader("Access-Control-Allow-Origin", origin);
}
注意:1. 发送的Cookie是被调用方的Cookie,不是调用方的Cookie document.cookie="cookie1=xiaofengqing"这样就能新加入一个cookie了 2.Access-Control-Allow-Origin不能使用*
四、带自定义头的跨域
//支持所有自定义头
String headers = req.getHeader("Access-Control-Request-Headers");
if(!StringUtils.isEmpty(headers)) {
res.addHeader("Access-Control-Allow-Headers",headers);
}