为什么会出现跨域:
1.浏览器限制 2.同源策略,协议(http,https),域名(www.xx.com),端口(80) 其中一个条件不一样就会出现跨域 3.XHR(XMLHttpRequest)请求才有的, 如果用其他方式请求则不会出现:如用一个图片加载的方式去加载一个get请求,不会出现跨域错误
<img src="http://localhost:8080/get"/>
解决思路
浏览器:设置浏览器让其允许跨域
找到chrome.exe 文件,使用命令,新打开一个允许跨域的浏览器
.\chrome --disable-web-security --user-data-dir=g:\temp3
重修请求界面,会发现浏览器已经允许跨域了
本地测试采取这种方式比较方便。
XHR:不用XHR发送请求, 换成JSONP请求
jsonp的跨域原理是浏览器发出的是script请求,script由jquery动态生成
返回类型也变成了 js脚本application/javascript
$.ajax({
url:"xx.com",
dataType: "jsonp",
jsonp:"callback",
success: function(){}
})
把请求的dataType换成jsonp,
浏览器发送请求的时候会带一个callback参数,服务器获得了callback参数后就知道这个是个jsonp请求,(这里jsonp参数是默认的callback),然后把本来json的数据,转换成js类型数据,返回回去。
动态生成的script
缺点:需要改动服务器 只支持GET请求,
跨域:被调用方,让服务器接口设置允许跨域,
调用方,做一个代理,发送请求的时候把指定url转到服务器域名里面
设置服务器端代码:
服务器端代码用springboot写的,修改方式如下
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
@SpringBootApplication
public class Demo1Application {
public static void main(String[] args) {
SpringApplication.run(Demo1Application.class, args);
}
// 在主函数里面添加过滤器
@Bean
public FilterRegistrationBean registerFilter() {
FilterRegistrationBean bean = new FilterRegistrationBean();
bean.addUrlPatterns("/*");
bean.setFilter(new CrosFilter());
return bean;
}
}
package com.example.demo;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;
public class CrosFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
// TODO Auto-generated method stub
HttpServletResponse res = (HttpServletResponse) response;
// 请求的域名匹配任意域名
res.addHeader("Access-Control-Allow-Origin", "*");
// 请求方法匹配任意方法
res.addHeader("Access-Control-Allow-Methods", "*");
chain.doFilter(request, response);
}
}
简单请求,非简单请求
浏览器在发送跨域请求的时候会根据请求 是否是简单请求来做不同的处理,简单请求就会先处理后判断,非简单请求就会先做预检命令处理后 在把命令发送出去
常见简单请求:
方法为:GET,HEAD,POST
请求header里面,无自定义头
content-type为以下几种:
text/plain,multipart/form-data, application/x-www-form-urlencoded
非简单请求:
put,delete方法的ajax请求
发送json格式的ajax请求
带自定义头的ajax请求
OPTIONS预检命令
缓存预检命令:Access-Control-Max-Age:3600 表示在一个小时内可以缓存预检命令,不需要重复发送
那些条件 "Access-Control-Allow-Origin", "*" 不能跨域
发送请求的时候携带了cookie时, Access-Control-Allow-Origin必须为指定的域名,不能用通配符* 同时 Access-Control-Allow-Credentials:true
$.ajax({
type:"get",
url: "xx",
xhrFields:{
widthCredentials: true // 发送请求的时候携带上cookie
},
success: function(){
}
})
在服务器端可以获取 发送方的域名Origin,然后设置到Access-Control-Allow-Origin 所有域名就都可以跨域了,包括传cookie的请求
HttpServletRequest req = (HttpServletRequest) request;
String origin = req.getHeader("Origin");
if(!org.springframework.util.StringUtils.isEmpty(origin)){
res.addHeader("Access-Control-Allow-Origin", origin);
}
Nginx配置代理服务器
1.修改window的hosts文件,配置一个本地的域名
添加一行127.0.0.1 a.com, 现在访问本地a.com,就可以访问本地127.0.0.1了
然后在nginx根目录创建一个vhost文件夹
然后再nginx.conf里面最后面添加
读取vhost文件里面的所有文件
再vhost里面创建a.com.config
写入如下内容
server{
listen 80; # 监听端口
server_name a.com; # 监听域名
location /{
proxy_pass http://location: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;
#$http_origin 可以获取到http请求头下面origin的值,然后写回到请求头里面
add_header Access-Control-Allow-Headers $http_access_control_request_headers;
#直接处理浏览器的预检命令,
if ($request_method==OPTIONS){
return 200;
}
}
}
启动nginx 运行nginx -t 测试运行,成功后nginx -s reload 重修载入 请求接口也换成a.com/8080/xx 就可以了
spring框架解决办法
隐藏跨域
本地hosts添加一个abc.com地址
nginx添加一个文件
server{
listen 80;
server_name abc.com;
location /{
proxy_pass http://localhost:8020/;
}
location /ajaxserver{
proxy_pass http://localhost:8080/test/;
}
}
配置成功后 nginx -s reload 重新加载后 访问本地abc.com 地址