跨域问题以及解决方案
一、什么是跨域
因为安全问题,非同源的网站发送请求数据,会被浏览器拒绝。
二、跨域的原因
就是跨域名,跨端口,跨协议。举个例子:
http://www.baidu.com/hello/world.html
请求地址 | 形式 | 是否跨域 |
---|---|---|
http://www.baidu.com/test/a.html | 同一域名,不同文件夹 | 非跨域 |
http://www.baidu.com/hello/kitty.html | 同一域名,同个文件夹 | 非跨域 |
http://test.baidu.com/hello/world.html | 非同个域名,文件夹一样也没用 | 跨域 |
http://www.baidu.com:8081/hello/world.html | 同一域名,端口不一样 | 跨域 |
https://www.baidu.com/hello/world.html | 不同的协议、同个域名,文件夹一样 | 跨域 |
域名要一致,端口要一致,协议(http、https要一致)才不是跨域。
三、如何解决ajax跨域
3.1、 jsonp
古老又神奇的解决方案,实际不推荐。如果要用,可以使用类似JQuery对jsonp封装好的类库来处理。
$.ajax({
url: "http://localhost:8080/student",
type: "GET",
dataType: "jsonp", //指定服务器返回的数据类型
success: function (data) {
console.log(JSON.stringify(data));//获取得到的数据
}
});
这时候你会在network看到https://tcc.taobao.com/cc/json/mobile_tel_segment.htm?callback=jQuery30003244305640945704_1531990977401&tel=15510588888&_=1531990977402
;
内部操作应该是生成一个全局的方法:jQuery30003244305640945704_1531990977401
成功的时候回调这个方法,也就是
jQuery30003244305640945704_1531990977401(data){
console.log(JSON.stringify(data));//获取得到的数据
}
当请求访问成功的时候会调用这个方法。之前用过一次,被动使用。主要是因为对接的技术方只提供这个方式,很显然他们说什么都对…
- 测试代码(DEMO来源:CSDN lulu19870526)
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>测试jsonp</h1>
<ul>
<li class="num">手机号码: <span></span></li>
<li class="province">归属省份: <span></span></li>
<li class="operators">运 营 商: <span></span></li>
<li class="num2">手机号码: <span></span></li>
<li class="province2">归属省份: <span></span></li>
<li class="operators2">运 营 商: <span></span></li>
</ul>
<script src="https://code.jquery.com/jquery-3.0.0.min.js"></script>
<script>
function testjsonpcallback(data){
var province = data.province,
operators = data.catName,
num = data.telString;
$('.num span').html(num);
$('.province span').html(province);
$('.operators span').html(operators);
}
$(function(){
$.ajax({
url: "http://tcc.taobao.com/cc/json/mobile_tel_segment.htm",
type: "GET",
data:{tel:"15510588888"},
dataType: "jsonp", //指定服务器返回的数据类型
success: function (data) {
console.log(JSON.stringify(data));//获取得到的数据
var province = data.province,
operators = data.catName,
num = data.telString;
$('.num2 span').html(num);
$('.province2 span').html(province);
$('.operators2 span').html(operators);
}
});
})
</script>
<script src ="http://tcc.taobao.com/cc/json/mobile_tel_segment.htm?tel=15510588888&callback=testjsonpcallback" type="text/javascript"></script>
</body>
</html>
- 优点
- 兼容性好,古董浏览器也能支持
- 后端不用特殊支持
- 缺点
- 只支持GET
- 只支持HTTP
- 返回没有状态码
3.2、后端配置允许跨域
- 后端过滤器
后端去掉所有的跨域过滤。生产环境请禁用跨域!
public void doFilter(ServletRequest req, ServletResponse res,
FilterChain chain) throws IOException, ServletException {
HttpServletResponse httpResponse = (HttpServletResponse) res;
res.setHeader("Access-Control-Allow-Origin", originHeader);
res.setContentType("application/json;charset=UTF-8");
res.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
res.setHeader("Access-Control-Max-Age", "3600");
res.setHeader("Access-Control-Allow-Headers", "Origin, No-Cache, X-Requested-With, If-Modified-Since, Pragma, Last-Modified, Cache-Control, Expires, Content-Type, X-E4M-With,userId,token");//表明服务器支持的所有头信息字段
res.setHeader("Access-Control-Allow-Credentials", "true"); //如果要把Cookie发到服务器,需要指定Access-Control-Allow-Credentials字段为true;
res.setHeader("XDomainRequestAllowed","1");
chain.doFilter(req, res);
return;
}
参数说明
"Access-Control-Allow-Origin"
表明它允许”http://demo.com“发起跨域请求,* 为所有"Access-Control-Max-Age"
表明在3628800秒内,不需要再发送预检验请求,可以缓存该结果"Access-Control-Allow-Methods"
表明它允许GET、PUT、DELETE的外域请求"Access-Control-Allow-Headers"
表明它允许跨域请求包含content-type头
前端使用
ajax
$.ajax({ url: config.url, xhrFields: { //请求支持跨域 withCredentials: true }, type: "GET", data:{tel:"15510588888"}, success: function (rs) {}, error: function (xhr, textStatus, errorThrown) {}, complete: function (data) {} })
fetch
fetch(url,{ method: 'POST', mode: 'cors',//跨域模式 credentials: 'include',//带上cookie headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: JSON.stringify(postData) })
3.3、nginx反向代理(推荐)
反向代理是不用改任何项目代码的,生产环境用的比较多。测试环境也可以使用webpack(node)
的代理,此处仅介绍nginx反向代理。
- 反向代理
- 图
- 发什么什么事?
- 客户端处于域
www.baidu.com
发起www.baidu.com/api
请求 - nginx 监听到了这个请求,中间代理给了
www.google.com/api
www.google.com/api
返回数据给nginx- nginx 返回数据给客户端;
- 客户端处于域
- 换成我们常用的开发环境(用nginx做容器):
- 客户端处于域
127.0.0.1:3000
发起127.0.0.1:3000/api
请求 - nginx 监听到了这个请求,中间代理给了
127.0.0.1:8080/api
127.0.0.1:8080/api
返回数据给nginx- nginx 返回数据给客户端;
- 客户端处于域
- 用node 或者 tomcat等容器
- 用nginx监听4000端口:
127.0.0.1:4000
,所有静态资源代理到127.0.0.1:3000
,所有动态资源(接口什么的)代理到127.0.0.1:8080
- 客户端处于域
127.0.0.1:3000
发起127.0.0.1:4000/api
请求 - nginx 监听到了这个请求,中间代理给了
127.0.0.1:8080/api
127.0.0.1:8080/api
返回数据给nginx- nginx 返回数据给客户端;
- 用nginx监听4000端口:
- 配置
server {
listen 4000;
#日志开启,方便调试Nginx配置
error_log logs/xxx.log notice;
rewrite_log on;
#正则匹配:~开头,原则是按配置顺序进行匹配。
#先匹配静态资源,剩余的发送给动态资源
# 所有请求 /api/结尾的都转发给后端服务
location ~ /api/{
set $context $1;
proxy_pass http://192.168.1.171:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header REMOTE-HOST $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
client_max_body_size 100m;
proxy_cookie_path ~.* /$context/;# cookie也有路径,路径不一样请求带的cookie可能丢失
}
# 拦截所有静态资源的请求到指定目录
location ~ ^/(.+\.(?:html|css|jpeg|jpg|png|gif|js|eot|otf|ttf|woff|svg))$ {
alias usr/front/dist/$1;
index index.html index.htm;
}
}
3.4 修改document.domain
前提条件:客户端与服务端必须在一个一级域名、协议、端口都要一致,否则无法修改document.domain
进行跨域。只能跨二级域名访问。
PS,一级域名:baidu.com
、二级域名:tieba.baidu.com
console.log(document.domain);//"blog.csdn.net"
//修改域
document.domain = "csdn.net";//document.domain : "csdn.net"
//修改域
document.domain = "baidu.com";
//Uncaught DOMException: Failed to set the 'domain' property on 'Document':
//'baidu.com' is not a suffix of 'csdn.net'.
修改domain只能做到近亲之间的跨域,跨度太大就不允许了。
酒要一口一口地喝,路要一步一步走,步子迈得太大,会扯着蛋–《让子弹飞》