什么是跨域? 为什么会出现跨域

浏览器为了保护用户, 保证用户安全,使用同源策略 来针对请求做出响应。
同源:
协议相同:protocol(ftp file http https 等协议)不同得协议被服务器认为不同源
域名相同:domain网站得域名必须一致。
端口相同:port默认80端口,但是不同端口也被认为跨域
以上任何不相同都被认为是跨域。

简单的说:
1 · 使用xmlHttpRequest,即我们通常说的ajax请求
2 · 浏览器做了这个事
3 · 访问的域名不同,即访问的html页面是a域名下的,但内部js发送的ajax请求的目标地址却是b域名

以上三个条件缺一不可,尤其是第三个条件,许多做移动端的同学可能都没有听过,因为移动端可以用各种http请求狂发不同的域名,但是浏览器不允许我们这么做

JavaScript只能访问和操作自己域下的资源,不能访问和操作其他域下的资源。跨域问题是针对JS和ajax的,html本身没有跨域问题,比如a标签、script标签、甚至form标签(可以直接跨域发送数据并接收数据)等。这是为了保证用户得安全。

说到底,跨域的原因是 前后端分离 ,跨域的目的是 安全


如何解决跨域问题(服务器代理, CORS,JSONP)

解决跨域问题的根本就是要打破上述的三个限制中的任何一个,我们可以逐个击破:

JSONP方式
jsonp是打破第一重限制,(因为)用了XMLHttpRequest就跨域,那不用这种方式了,我们来看一段jquery的jsonp格式的ajax请求:

$.ajax({
type : "GET",
url : "http://api.map.baidu.com/geocoder/v2/",
data:"address=河南",
dataType:"jsonp",
jsonp:"callback",
jsonpCallback:"showLocation",
success : function(data){
alert("成功");
},
error : function(data){
alert("失败");
}
});

看似用了ajax请求,其实内部完全不是那么回事,多了jsonp和jsonpCallback选项,它内部将代码翻译并把页面上的dom操作成这样:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<script type='text/javascript'>
// 后端返回直接执行的方法,相当于执行这个方法,由于后端把返回的数据放在方法的参数里,所以这里能拿到res。
window.showLocation = function (res) {
console.log(res)
//执行ajax回调
}
</script>
<script src='http://api.map.baidu.com/geocoder/v2/?address=河南&callback=showLocation' type='text/javascript'></script>
</body>
</html>

这个时候,html页面的script src标签回去访问api.map.baidu.com的服务端,由于script,img这种标签浏览器是不受xmlhttprequest限制的,可以随意访问,这个时候对应的后端代码取得address参数,最后根据双方约定好的callback参数,返回一个被包装后的json

咦,上面我们为什么使用script标签?

script标签src允许跨域,我们利用script标签得src来访问资源,然后使用callback来承接返回得数据,并处理。

let input = document.queryselector("input")
input.oninput = function(){
let script = document.createElement("script")
let url = `https://s.search.bilibili.com/main/suggest?jsoncallback=mycallback&term=${input.value}`
script.src = url
document.body.appendChild(script)
script.onload = () => document.body.removeChild(script)
}
function mycallback(data){
console.log(data)
}

其中url是我们需要请求得src,在src中拼接了一个完整得get请求,包括请求得参数请求时发送的数据,以及回调接口。
将script添加到页面中的时候,script的代码会立即解析执行,执行完成之后删除(若是原生封装的ajax,记得手动删除removeChild)。其中mycallback是本地的函数,这个函数接受参数用于使用参数。

CORS 后台允许跨域
在后台添加允许跨域:“跨域资源共享”(Cross-origin resource sharing)。它允许浏览器向跨源(协议 + 域名 + 端口)服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。这里以node的koa框架为例:

//先npm并引入:const cors=require('koa2-cors')
app.use(cors({
origin:['能够跨域ajax的url'],
credentials:true
}))

第三方(也就是代理服务器)

文章https://m.imooc.com/article/288397中是这样说的:打破不同源的限制,我只要让它同源就可以了,比如要我的静态页面是 http://a.com/index.html 动态ajax请求访问的是http://b.com/api/***
我只需要将对应的服务部署在不同的机器上,然后使用一个公共的c.com的域名作为nginx反向代理的入口域名,在将静态服务和动态服务分别挂在后面的被代理局域网服务器内,修改配置


下面我们来详细看下nginx反向代理 的实战操作:

server
{
listen 3002;
server_name localhost;
location /ok {
proxy_pass http://localhost:3000;

# 指定允许跨域的方法,*代表所有
add_header Access-Control-Allow-Methods *;

# 预检命令的缓存,如果不缓存每次会发送两次请求
add_header Access-Control-Max-Age 3600;
# 带cookie请求需要加上这个字段,并设置为true
add_header Access-Control-Allow-Credentials true;

# 表示允许这个域跨域调用(客户端发送请求的域名和端口)
# $http_origin动态获取请求客户端请求的域 不用*的原因是带cookie的请求不支持*
add_header Access-Control-Allow-Origin $http_origin;

# 表示请求头的字段 动态获取
add_header Access-Control-Allow-Headers
$http_access_control_request_headers;

# OPTIONS预检命令,预检命令通过时才发送请求
# 检查请求的类型是不是预检命令
if ($request_method = OPTIONS){
return 200;
}
}
}