文章概述
本篇文章细说ajax跨域。
跨域
跨域指的是web网站访问与自己域名不同的其它地址的请求;
实质
跨域实质是浏览器安全验证限制了跨域请求;
1 | 当浏览器发出跨域请求时,浏览器会在请求头中添加相关的跨域信息,经过服务器处理后,响应头里如果没有对应的跨域信息,将会发生跨域失败; |
浏览器处理请求
两种情况:简单请求和非简单请求;
浏览器在发送跨域请求时,对简单请求会直接执行,对非简单请求会先发出一个OPTION预检命令,校验通过后再执行请求;
简单请求
- 请求方法为:get/head/post
- 请求头里:无自定义头,Content-Type为:text/plain、multipart/form-data、application/x-www-form-urlencoded
非简单请求
- put/delete方法的ajax请求;
- 发送json格式的ajax请求;
- 带自定义请求头的ajax请求;
http请求处理流程
请求从浏览器发出,请求到达http服务器(主要apache或nginx来处理),静态请求(如图片等)会被http服务器处理后直接返回,动态请求则会被转发到后台的应用服务(tomcat)来处理,处理完结果会发给http服务器,http服务器再转给浏览器。
产生跨域的条件
以下三个条件同时发生时,才会发生跨域问题:
1> 浏览器限制:
浏览器出于安全考虑,当发现你的请求跨域的时候,会主动做安全校验,如果校验不通过就会报跨域安全问题,如果后台没有任何限制,此时后台是接收到了请求并正确返回的,只是浏览器自身报错;
2> 跨域;
服务器后台不允许前台调用;
3> XHR(XMLHttpRequest)请求
浏览器发出的请求如果不是XHR请求,则不会进行安全验证,ajax默认发出的请求就是XHR请求;
前端跨域示例
定义不同域的基本地址base,有以下几种跨域请求示例;
普通跨域请求
1 | $.getJSON(base + "/get").then(function (jsonObj) { |
jsonp
1 | $.ajax({ |
非简单请求跨域
json格式请求,预检命令与缓存预检命令
1 | $.ajax({ |
带cookie跨域
1 | $.ajax({ |
自定义请求头跨域
1 | $.ajax({ |
解决跨域的思路
- 客户端改动:让浏览器不做安全验证;
- 控制发出去的请求非XHR类型,JSONP方式通过动态创建script,在script中发出跨域请求,弊端是只能发Get请求;
- 实现跨域:
1 | 1> 被调方(一般是服务端)做修改,支持跨域,被调用方通知调用方的浏览器跨域允许调用方跨域访问; |
禁用浏览器限制解决跨域
windows环境,cmd命令行进入打开chrome.exe文件路径,输入以下命令启动一个没有安全验证的chrome浏览器,即可跨域请求。
1 | $ chrome --disable-web-security --user-data-dir=f:\temp |
Jsonp跨域
发送非xhr类型的请求,浏览器不会做安全校验;
概述
- Jsonp(json with padding) ,是json的补充使用方式,利用script标签请求资源可以跨域来解决跨域问题的。jsonp通过动态创建script,在script里面把请求发出的;
- 普通的ajax请求请求和返回的类型Content-Type是json,jsonp请求方式,是javascript;
- 改动范围:前后台都需要改动;
注意:Jsonp请求返回的数据结构是:callback的参数值作为函数名,返回的数据作为参数;
请求步骤
- 前端:jsonp的请求方式,默认自动给请求加了callback参数,后台发现有callback就认为是jsonp请求。
- 后台:ajax发送jsonp请求可以指定参数名,对应后台也需要修改,来识别对应的参数名;
前端发Jsonp请求
1 | // 定义请求 |
后端改动
spring-boot
java后台,spring-boot框架;
添加Jsonp支持类
在项目中添加Jsonp支持类:JsonpAdvice1
2
3
4
5
6
public class JsonpAdvice extends AbstractJsonpResponseBodyAdvice {
public JsonpAdvice() {
super("callback");
}
}
缺点
- 需要服务端修改代码;
- 只支持get请求;
- 发送的不是XHR请求;
支持跨域
支持跨域的两种方式:
- 被调用方支持跨域(前端访问后端的地址是绝对地址);
- 调用方隐藏跨域(前端访问后端的地址是相对地址);
被调用方服务支持跨域
前端访问后端的地址是绝对地址
请求处理流程
调用方请求从浏览器发出,直接请求被调用方http服务器,被调用方的http服务器根据动态请求则会交给应用服务器(tomcat)来处理,处理完成后,结果传给http服务器,最后发给浏览器。
跨域思路
被调用方服务器根据http请求协议,在响应头中返回一些标志字段,来告诉浏览器允许调用方跨域请求。
JavaEE架构里配置跨域响应头:
1 | 1. 应用服务器过滤请求,添加跨域响应头; |
Filter跨域
概述
- Filter方案其实是调用放的浏览器直接发送请求到被调用方的应用服务器,应用服务器通过filter增加响应头的方式处理请求直接返回给调用方浏览器;
- Filter方案过滤请求头信息,响应给浏览器允许跨域的信息;
后端过滤方案
springboot
通过过滤所有请求,来添加响应头,来实现全局接口跨域;
1> 在application程序入口中添加url请求拦截入口方法:
1 | @SpringBootApplication |
1 | import org.springframework.util.StringUtils; |
nginx跨域
此处指后端对应的nginx http服务器。
虚拟主机
虚拟主机是:多个域名指向同一个服务器,服务器根据不同的域名吧请求转到不同的应用服务器,看上去有很多个主机,实际上只有一个主机。
配置跨域虚拟主机
按以下步骤配置支持跨域的虚拟主机,即可完成简单的nginx服务支持跨域;
1> 在host文件中添加本地域名映射
(c:\Windows\System32\Drivers\etc\host),主要目的是通过xem.com域名在本地模拟访问后端服务器;;
1 | # 即访问xem.com域名时,映射到本机地址; |
2> nginx\conf目录下创建vhost文件,用于存放每个虚拟主机域名的conf文件,并修改nginx配置文件nginx/conf/nginx.conf,添加以下代码,表示加载vhost文件夹里的虚拟主机配置:
1 | http{ |
3> 配置虚拟主机:vhost文件夹中,新建一个xem.com.conf的域名虚拟主机配置文件,用于监听该域名的请求,将请求转到相应的应用服务器;
1 | server{ |
4> 启动nginx校验跨域;
nginx根目录下,执行如下相关nginx命令:
- 校验配置文件书写是否正确
1 | $ nginx.exe -t |
- 启动nginx
1 | $ start nginx.exe |
- 重新加载nginx改动
1 | $ nginx.exe -s reload |
- 停止nginx服务
1 | $ nginx.exe -s stop |
apache跨域
apache跨域和nginx原理一样,也是配置虚拟主机来设置响应头,最终支持跨域的;
配置步骤
- 本地host文件,添加xem.com域名映射,访问此域名时模拟访问后端服务器;
- 配置apache配置文件:打开Apache24\conf\httpd.conf配置文件,开启相关配置功能;
打开以下注释:
1 | # 虚拟主机模块 |
- 配置虚拟主机:打开虚拟主机配置文件Apache24\conf\extra\httpd-vhosts.conf;
添加一个虚拟主机:
1 | <VirtualHost *:80> |
- 启动apache服务,验证跨域;
方式一: 命令方式启动;1
2
3
4// 启动服务
$ httpd.exe -k start
// 停止服务
$ httpd.exe -k stop
方式二:直接打开Apache24\bin\httpd.exe执行文件;
JavaEE后端跨域
spring-boot框架
过滤请求跨域
通过过滤所有url请求的方式,实现全局接口支持跨域;
详见Filter跨域;
全局接口跨域注解
- spring-boot 1.0的实现方式:
1 | // 新建配置类,配置跨域信息; |
- spring-boot 2.0的实现方式:
1 | // 新建配置类,配置跨域信息; |
局部接口跨域注解
通过@CrossOrigin注解让部分接口Controller类或接口方法支持跨域;
- 只需要在接口controller上或某个接口方法上加@CrossOrigin注解即可;
- 配置@CrossOrigin并不支持cookie的请求,如需支持cookie,需要配置额外信息:@CrossOrigin(allowCredentials=”true”);
1 |
|
调用方服务隐藏跨域
隐藏跨域是在调用方的http服务器进行配置,前端访问后端的地址是相对地址;
- 隐藏跨域指调用方的请求从调用方的http服务器直接发送到被调用了方的http服务器;
- 跨域请求是通过调用方http服务器的反向代理,将请求转发到被调用方的http服务器的,所以在浏览器上面看不到任何跨域请求的信息;
反向代理:访问同一个域名的两个不同的url,最后会去到两个不同的服务器;
nginx
前端http服务器是nginx服务器,配置nginx隐藏跨域;
1> host文件中,添加xem.com作为本地域名映射;
2> nginx/conf/vhost文件中添加域名虚拟主机配置:
1 | server{ |
3> ajax请求使用相对地址“/ajaxserver”拼接具体请求;
4> 启动nginx服务,访问xem.com网页,请求会自动转发到后台的地址;
apache
1> 设置host文件中的域名映射;
2> 同被调用方支持跨域里的apache的配置类似,不同的是虚拟主机的配置,如下:
1 | <VirtualHost *:80> |
3> 网页使用相对地址请求服务端接口;
4> 启动apache服务测试跨域;
第三方js库解决跨域
在编写angular/reactjs/vue项目时,有他们自己的跨域解决方案;