什么是跨域
跨域是指从一个域的网页去请求另一个域的资源。只要协议,域名,端口有任何一个的不同,就被当作跨域。
js跨域是指通过js在不同的域之间进行数据传输或通信。比如用ajax向一个不同的域请求数据,或者通过js获取页面中不同域的iframe中的数据。
跨域并不是请求发不出去,请求能发出去,服务端能收到请求并正常返回结果,只是结果被浏览器拦截了。
iframe
利用iframe标签,我们可以在自己的网站页面加载别人网站或者本站其他页面的内容。
src规定了在iframe中显示的文档的url。scrolling规定是否显示页面滚动条。
为什么浏览器要限制跨域访问呢
如果一个网页可以随意的访问另外一个网站的资源,浏览器很容易受到XSS,CSRF等攻击。
浏览器出于安全考虑,有了同源策略,限制跨域访问。
跨域的方法
跨域资源共享(CORS)
一般只要服务端实现CORS接口,就可以跨域。
简单请求
1. 普通跨域请求。只要服务器端响应头设置Access-Control-Allow-Origin,表明哪些域可以访问资源,如果设置通配符则表示所有网站可以访问资源。如果浏览器检测到服务端相应的设置,就可以允许Ajax进行跨域的访问。
2. 如果是带cookie请求的跨域,在客户端也要进行设置。在ajax方法中加上
并且要在服务器响应头中的Access-Control-Allow-Origin指定发送请求的域名,不能用 * 。这样可以实现一个需要登录到另一个系统才可以看见页面的跨域(带cookie的跨域)。
复杂请求:先发送OPTIONS预检请求
对那些可能对服务器数据产生副作用的HTTP请求方法(特别是GET以外的HTTP请求),浏览器必须首先使用OPTIONS方法发起一个预检请求,从而获知服务端是否允许该跨域请求。
浏览器先询问服务器,当前网页所在域名是否在服务器的许可名单之中,以及可以使用哪些HTTP动词和头信息字段。只有得到肯定答复,浏览器才会发出正式的XMLHttpRequest请求,否则就报错。
预检请求的请求方法是OPTION。头信息包括,Access-Control-Request-Method,Access-Control-Request-Headers。
预检请求的回应:Access-Control-Allow-Origin,Access-Control-Allow-Headers,Access-Control-Allow-Credentials(是否需要携带身份凭证Cookies),Access-Control-Max-Age。
服务器通过预检请求以后,浏览器开始发送正常的CORS请求,但要加上Origin头信息字段(浏览器的域);服务器的回应也要包含Access-Control-Allow-Origin头信息字段
JSONP
JSONP原理
利用 script 标签没有跨域限制的漏洞,网页可以得到从其他来源动态产生的JSON数据。JSONP请求一定需要对方的服务器做支持才可以。
JSONP的实现流程
-
声明一个回调函数,其参数名(如show)当作参数值,要传递给跨域请求数据的服务器,函数形参为 要从服务器获取的目标数据
-
创建一个 script 标签,把那个跨域的API数据接口地址,赋值给script的src,还要在这个地址中向服务器传递该函数名(可以通过问号传参 ?callback=show )
-
服务器接收到请求后,需要进行特殊的处理:把传递进来的函数名和它需要给你的数据拼接成一个字符串。例如:传递进去的函数名是show,它准备好的数据是 show('数据')
-
最后服务器把准备的数据通过HTTP协议返回给客户端,客户端再调用执行之前声明的回调函数(show),对返回的数据进行操作
手写JSONP
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<script>
/**
* 手写jsonp并返回Promise对象
* 参数url,data:json对象,callback函数
*/
function jsonp(url, data = {}, callback = 'callback') {
// 处理json对象,拼接url
data.callback = callback
let params = []
for (let key in data) {
params.push(key + '=' + data[key])
}
console.log(params.join('&'))
// 创建script元素
let script = document.createElement('script')
script.src = url + '?' + params.join('&')
document.body.appendChild(script)
// 返回promise
return new Promise((resolve, reject) => {
window[callback] = (data) => {
try {
resolve(data)
} catch (e) {
reject(e)
} finally {
// 移除script元素
script.parentNode.removeChild(script)
console.log(script)
}
}
})
}
jsonp('http://photo.sina.cn/aj/index', {
page: 1,
cate: 'recommend'
}, 'jsoncallback').then(data => {
console.log(data)
})
</script>
</body>
</html>
CORS和JSONP对比
1. JSONP只能实现GET请求,而CORS支持所有类型的HTTP请求。
2. 使用CORS,开发者可以使用普通的XMLHttpRequest发起请求和获得数据,比起JSONP有更好的错误处理。
3. JSONP主要被老的浏览器支持,它们往往不支持CORS;而绝大多数现代浏览器都已经支持了CORS。