一、概念
1. 跨域问题产生原因:
① 浏览器限制
② XHR(XMLHttpRequest)请求
2. 解决思路:
二、简单的解决方案
1. 禁止浏览器检查:
在命令行模式下,键入如下命令打开谷歌浏览器,可以禁止浏览器检查,避免跨域问题。
chrome --disable-web-security --user-data-dir=g:\google-temp
注:谷歌浏览器49版本号之前只需要输入 chrome --disable-web-security ,49版本号之后需要多输入 --user-data-dir=g:\google-temp,这个文件夹是我们自己新建的,相当于一个保存个人信息的文件夹不能使用谷歌默认的文件夹。
2. Jsonp方式:
前端发送Jsonp请求,后端也需做响应修改,但是在SpringBoot 2.x以后已被弃用。
Jsonp的弊端:
1. 服务端需要改动代码支持
2. 只支持get请求
3. 发送的不是XHR请求(XHR请求支持一些特性,诸如异步、事件)
三、从架构的角度解决
1. 常见的JAVAEE架构解决方案:
一次请求的基本流程:客户端的请求首先发送到Http服务器(Apache或Nginx等),Http服务器会判断客户端的请求是静态请求(不涉及用户数据的请求,诸如图片、js、css等)还是动态请求(调用后端接口)。
如果是静态请求,Http服务器处理完毕后直接返回给客户端,不用经过Tomcat等应用服务器;如果是动态请求,Http服务器转发请求给Tomcat等应用服务器,Tomcat处理完毕后返回给Http服务器,最后Http服务器再返回给客户端。
2. 被调用方解决跨域 - Filter解决:
2.1 简单请求和非简单请求:
在具体解决跨域问题之前,有一个重要的概念首先要了解,就是简单请求和非简单请求。
简单请求:请求方法为GET、HEAD、POST。请求头无自定义头,Content-Type为text/plain、multipart/form-data、application/x-www-form-urlencoded之一。
非简单请求:请求方法为PUT、DELETE。发送Json格式参数的Ajax请求,带自定义头的Ajax请求。
了解了简单请求和非简单请求的区别之后,这些会对跨域问题有什么影响呢?
答:对于简单请求来说,请求会直接发送到服务端;而对于非简单请求来说,浏览器会预检查,如下:
postJson这个请求第一次是OPTIONS,这代表浏览器会预检查,检查成功后才会发到服务端(如图,第二次是POST请求)。当我们的请求是非简单请求时,服务端在响应头补充设置Access-Control-Allow-Headers和Access-Control-Max-Age(浏览器缓存信息的时长)。
2.2 不涉及Cookie的请求:
思路:在服务端设置响应头(HttpServletResponse)来解决跨域问题。
代码解释:
Access-Control-Allow-Origin:调用方(客户端)的域名,* 代表所有。
Access-Control-Allow-Methods:调用方(客户端)请求的方法,如GET、POST、PUT、DELETE等,* 代表所有。
注:如果只是个别接口需要设置跨域,也可直接在Controller层设置响应头(HttpServletResponse),如下:
2.3 涉及Cookie的请求:
思路:涉及Cookie的跨域问题,服务端设置响应头时要注意,Access-Control-Allow-Origin要设置具体的域名,不能使用 * 代替。代码如下:
注:Cookie是被调用方的。
3. 被调用方解决跨域 - Nginx解决:
3.1 第一步:配置host文件
b.com 代表我们被调用方的域名。
3.2 第二步:配置Nginx
Nginx常用命令(Win环境):
1. 测试Nginx配置文件是否正确
nginx.exe -t
2. 启动Nginx
start nginx.exe
3. 重启Nginx(一般用于修改配置文件后)
nginx.exe -s reload
4. 停止Nginx
nginx.exe -s stop
3.3 第三步:启动测试
启动前端、Nginx、服务端,测试结果如下:
注:通过响应头里的Server:nginx/1.16.0,我们可以看出 http://b.com/test/get1 是经Nginx转发到Tomcat又返回给前端的。我们平时也可观察Server字段的值,就能得知是否使用了Nginx这种Http服务器(例如,当Server: Apache-Coyote/1.1时,可知并没有使用Nginx,只使用了Tomcat)。
4. 被调用方解决跨域 - Apache解决:
4.1 第一步:配置host文件
b.com 代表我们被调用方的域名。
4.2 第二步:配置httpd.conf
下载解压缩Apache2.4,打开conf/httpd.conf文件,打开我们需要的模块:
LoadModule headers_module modules/mod_headers.so
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_http_module modules/mod_proxy_http.so
LoadModule rewrite_module modules/mod_rewrite.so
LoadModule vhost_alias_module modules/mod_vhost_alias.so
4.3 第三步:配置httpd-vhosts.conf
打开conf/extra/httpd-vhosts.conf文件,配置虚拟主机:
4.4 第四步:启动测试
执行bin/httpd.exe:
5. 被调用方解决跨域 - Spring解决:
利用spring-web包里的 @CrossOrigin注解配置:
1. 配置到类上,类里的所有方法都允许跨域
2. 配置到方法上,该方法允许跨域
3. 允许cookie的跨域配置
6. 调用方解决跨域 - Nginx解决:
反向代理被调用方的应用服务器。
6.1 第一步:配置host文件
a.com 代表我们调用方的域名。
6.2 第二步:配置Nginx
6.3 第三步:修改前端页面地址
把绝对地址改为相对地址。
6.4 第四步:启动测试
注:这样前端的请求就是本域请求,就不存在跨域问题了。
7. 调用方解决跨域 - Apache解决:
反向代理被调用方的应用服务器。
7.1 第一步:配置host文件
a.com 代表我们调用方的域名。
7.2 第二步:配置httpd-vhosts.conf
7.3 第三步:修改前端页面地址
7.4 第四步:启动测试
执行bin/httpd.exe:
四、总结
该项目代码仓库地址:https://gitee.com/wxj1994/cross-ajax