发生跨域问题的原因:
-
浏览器的限制,出于安全考虑。前台可以正常访问后台,浏览器多管闲事报跨域问题,但其实前台已经访问到后台了。
-
跨域,协议、域名、端口任何一个不一样浏览器就认为是跨域。
-
XHR(XMLHttpRequest)请求,因为ajax是XHR请求,浏览器就会捕捉跨域问题。
简单请求和非简单请求:
-
简单请求 GET HEAD POST,请求header里面:无自定义头,Content-Type为以下几种:text/plain,multipart/form-data,application/x-www-form-urlencoded。
-
非简单请求 put,delete方法的ajax请求,发送json格式的ajax请求,带自定义头的ajax请求。
解决思路:
-
让浏览器不做限制,指定参数,让浏览器不做校验,但该方法不太合理,它需要每个人都去做改动。
-
不要发出XHR请求,这样就算是跨域,浏览器也不会报错,解决方案是JSONP,通过动态创建一个script,通过script发出请求。
-
在跨域的角度:一种是被调用方修改代码,加上字段,告诉浏览器,支持跨域,支持调用。
- 通过nginx代理方式,在a域名里面的的请求地址使用代理指定到b域名。
跨域解决方案:
-
被调用方解决:在请求响应头增加指定字段,告诉浏览器允许调用。这种解决方案的请求是直接从浏览器发送的。(服务器端实现、NGINX配置Apache配置)。
-
调用方解决:这是隐藏跨域的解决法案。这种跨域请求不是直接从浏览器发送的,而是从中间的http服务器转发过去的。
设置浏览器不做限制:
-
可以使用everyting软件搜索浏览器的全路径,使用dos切换到此路径下 cd C:UsersAdministratorAppDataLocalGoogleChromeApplication
-
输入 chrome --disable-web-security --user-data-dir=g: emp3(g磁盘 temp3数据存储文件可随便创建)。
使用jsonp解决:
-
Jsonp(JSON with Padding) 是 json 的一种"使用模式",可以让网页从别的域名(网站)那获取资料,即跨域读取数据。为什么我们从不同的域(网站)访问数据需要一个特殊的技术(JSONP )呢?这是因为同源策略。同源策略,它是由Netscape提出的一个著名的安全策略,现在所有支持JavaScript 的浏览器都会使用这个策略。
-
前端ajax方法请求设置 dataType: "jsonp", jsonp: "callback", cache: true
-
后台需要增加JsonpAdvice 类
- jsonp只支持GET
- 服务器需要改动代码
@ControllerAdvice public class JsonpAdvice extends AbstractJsonpResponseBodyAdvice { public JsonpAdvice() { // TODO Auto-generated constructor stub // 这的名称需要和ajax中jsonp: "callback"设置的一样 super("callback"); } }
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>跨域</title> <script src="jquery-1.11.3.js"></script> <link rel="stylesheet" type="text/css" href="jasmine-2.8.0/jasmine.css"> <script src="jasmine-2.8.0/jasmine.js"></script> <script src="jasmine-2.8.0/jasmine-html.js"></script> <script src="jasmine-2.8.0/boot.js"></script> </head> <body> <script> function get1() { $.getJSON("http://localhost:8080/test/get1").then(function (result) { console.log(result); }); } // 每一个测试用例的超时时间 jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000; // 请求的接口的前缀 // http://localhost:8080/test var base = "/ajaxserverapache"; //测试模块 describe("跨域", function () {// 测试方法 it("jsonp请求", function (done) { // 服务器返回的结果 var result; $.ajax({ url: base + "/get1", dataType: "jsonp", jsonp: "callback", cache: true, success: function (json) { result = json; } }); // 由于是异步请求,需要使用setTimeout来校验 setTimeout(function () { expect(result).toEqual({ "data": "get1 ok" }); // 校验完成,通知jasmine框架 done(); }, 100); }); }); </script> </body> </html>
filter解决方案(被调用方):
-
在springBoot启动类中设置filter类过滤
-
设置具体的过滤参数信息
@SpringBootApplication public class AjaxserverApplication { public static void main(String[] args) { SpringApplication.run(AjaxserverApplication.class, args); } @Bean public FilterRegistrationBean registerFilter() { FilterRegistrationBean bean = new FilterRegistrationBean(); bean.addUrlPatterns("/*"); // 设置自定义的filter类 bean.setFilter(new CrosFilter()); return bean; } }
public class CrosFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { // TODO Auto-generated method stub } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { // TODO Auto-generated method stub HttpServletResponse res = (HttpServletResponse) response; HttpServletRequest req = (HttpServletRequest) request; String origin = req.getHeader("Origin"); if (!org.springframework.util.StringUtils.isEmpty(origin)) { //带cookie的时候,origin必须是全匹配,不能使用* res.addHeader("Access-Control-Allow-Origin", origin); } res.addHeader("Access-Control-Allow-Methods", "*"); String headers = req.getHeader("Access-Control-Request-Headers"); // 支持所有自定义头 if (!org.springframework.util.StringUtils.isEmpty(headers)) { res.addHeader("Access-Control-Allow-Headers", headers); }
// 设置预检命令一小时内不需要再发送 res.addHeader("Access-Control-Max-Age", "3600"); // enable cookie res.addHeader("Access-Control-Allow-Credentials", "true"); chain.doFilter(request, response); } @Override public void destroy() { // TODO Auto-generated method stub } }
nginx解决方案(被调用方):
-
在nginx文件中创建一个挂载文件夹vhost(创建a.conf配置文件)
-
在nginx.conf配置文件中引入vhost文件夹下conf结尾的配置文件 include vhost/*.conf;
- 配置如下(nginx -t检测配置文件是否正确 之后重载配置文件 nginx -s reload)
server{ listen 80; server_name a.com; location /{ proxy_pass http://localhost:8080/; add_header Access_Control-Allow-Methods *; add_header Access_Control-Max-Age 3600; add_header Access_Control-Allow-Credentials true; add_header Access_Control-Allow-Origin $http_origin; add_header Access_Control-Allow-Headers $http_access_control_request_headers; if ($request_method = OPTIONS){ # 判断是否是预检命令 return 200; } } }
Apache服务解决:
-
打开Apache24confhttpd.conf配置文件
-
放开注释的配置:
-
LoadModule vhost_alias_module modules/mod_vhost_alias.so
-
Include conf/extra/httpd-vhosts.conf
-
LoadModule proxy_module modules/mod_proxy.so
-
LoadModule proxy_http_module modules/mod_proxy_http.so
-
LoadModule headers_module modules/mod_headers.so
-
LoadModule rewrite_module modules/mod_rewrite.so
-
打开Apache24confextrahttpd-vhosts.conf配置文件,在最下边添加配置
- 重启服务
<VirtualHost *:80> ServerName a.com ErrorLog "logs/a.com-error.log" CustomLog "logs/a.com-access.log" common ProxyPass /http://localhost:8080/ # 把请求头的origin值返回到Access-Control-Allow-Origin字段 Header always set Access-Control-Allow-Origin 'expr=%{req:origin}' # 把请求头的Access-Control-Allow-Headers值返回到Access-Control-Allow-Headers字段 Header always set Access-Control-Allow-Headers "expr=%{req:Access-Control-Request-Headers}" Header always set Access-Control-Allow-Methods "*" Header always set Access-Control-Allow-Credentials "true" Header always set Access-Control-Max-Age "3600" #处理预检命令 RewriteEngine On RewriteCond %{REQUEST_METHOD}OPTIONS RewriteRule ^(.*)$"/" [R=204,L] </VirtualHost>
spring解决:
-
String 框架解决方案: @CrossOrigin 注解添加类上面
被调用方隐藏跨域解决Apache服务解决:
打开Apache24confextrahttpd-vhosts.conf配置文件,在最下边添加配置
重启服务
<VirtualHost *:80> ServerName a.com ErrorLog "logs/a.com-error.log" CustomLog "logs/a.com-access.log" common #被调用方接口 ProxyPass /ajaxserverapache http://localhost:8080/test #调用方 ProxyPass /http://localhost:8081/ </VirtualHost>
被调用方隐藏跨域解决nginx:
-
在nginx文件中创建一个挂载文件夹vhost(创建a.conf配置文件)
-
在nginx.conf配置文件中引入vhost文件夹下conf结尾的配置文件 include vhost/*.conf;
-
页面中接口访问前缀替换成下配置的相对路径/ajaxserver
-
配置如下(nginx -t检测配置文件是否正确 之后重载配置文件 nginx -s reload)
server{
listen 80;
server_name a.com;
location /{
# 调用方
proxy_pass http://localhost:8081/;
}
location /ajaxserver{
# 被调用方接口
proxy_pass http://localhost:8080/test;
}
}