本文已参与「新人创作礼」活动,一起开启掘金创作之路。
跨域
跨域的原因是什么? 当一个资源从与该资源本身所在服务器中不同域、协议、端口请求一个资源时,出于安全原因,浏览器限制从脚本内发起的跨源HTTP请求,XMLHttpRequest和Fetch API。
引入这个机制主要是用来防止CSRF攻击的(利用用户的登录态发起恶意请求)。
没有同源策略的情况下,A 网站可以被任意其他来源的 Ajax 访问到内容。如果你当前 A 网站还存在登录态,那么对方就可以通过 Ajax 获得你的任何信息。(当然跨域并不能完全阻止 CSRF)
解决跨域方式 JSONP JSONP 的原理很简单,就是利用
通过
<script src=""></script>
<script>
function jsonp(data) {
console.log(data)
}
</script>
JSONP
JSONP 使用简单且兼容性不错,但是只限于 get 请求。
在开发中可能会遇到多个 JSONP 请求的回调函数名是相同的,这时候就需要自己封装一个 JSONP,以下是简单实现:
function jsonp(url, jsonpCallback, success) {
let script = document.createElement('script')
script.src = url
script.async = true
script.type = 'text/javascript'
window[jsonpCallback] = function(data) {
success && success(data)
}
document.body.appendChild(script)
}
jsonp('', 'callback', function(value) {
console.log(value)
})
CORS
CORS 需要浏览器和后端同时支持。IE 8 和 9 需要通过 XDomainRequest 来实现。
浏览器会自动进行 CORS 通信,实现 CORS 通信的关键是后端。只要后端实现了 CORS,就实现了跨域。服务端设置 Access-Control-Allow-Origin 就可以开启 CORS。 该属性表示哪些域名可以访问资源,如果设置通配符则表示所有网站都可以访问资源。
虽然设置 CORS 和前端没什么关系,但是通过这种方式解决跨域问题的话,会在发送请求时出现两种情况,分别为简单请求和预检请求。另外,规范要求,对那些可能对服务器数据产生副作用的 HTTP 请求方法(特别是 GET 以外的 HTTP 请求,或者搭配某些 MIME 类型的 POST 请求),浏览器必须首先使用 OPTIONS 方法发起一个预检请求(preflight request),从而获知服务器是否允许该跨域请求。
服务器确认允许之后,才发起实际的HTTP请求。 在预检请求的返回中,服务器端也可以通知客户端,是否需要携带身份凭证(包括 cookie 和 HTTP 认证相关数据)。
如果发起请求时设置WithCredentials标志设置为 true,从而向服务器发送cookie,但是如果服务器的响应中未携带Access-Control-Allow-Credentials: true,浏览器将不会把响应内容返回给请求的发送者。
对于附带身份凭证的请求,服务器不得设置 Access-Control-Allow-Origin的值为*,必须是某个具体的域名。
注意,简单 GET 请求不会被预检;如果此类带有身份凭证请求的响应中不包含该字段,这个响应将被忽略掉,并且浏览器也不会将相应内容返回给网页。
document.domain
该方式只能用于二级域名相同的情况下,比如 a.test.com 和 b.test.com适用于该方式。
只需要给页面添加 document.domain = 'test.com' 表示二级域名都相同就可以实现跨域了。
postMessage
通常用于获取嵌入页面中的第三方页面数据。一个页面发送消息,另一个页面判断来源并接收消息。
// 发送消息端
window.parent.postMessage('message', '')
// 接收消息端
var mc = new MessageChannel()
mc.addEventListener('message', event => {
var origin = event.origin || event.originalEvent.origin
if (origin === '') {
console.log('验证通过')
}
})
iframe的跨域
用iframe的获取方式: 只能显示,不能控制
a.location.hash
b.window.name
iframe+postMessage
window.postMessage(data,origin)
子页面向父页面传递数据,则在子页面中调用父级window的postMessage
window.parent.postMessage=function(data,origin)