看啥推荐读物
专栏名称: charllote
前端开发工程师
目录
相关文章推荐
今天看啥  ›  专栏  ›  charllote

老生长谈 跨域问题

charllote  · 掘金  ·  · 2020-03-26 05:41
阅读 175

老生长谈 跨域问题

前端打工仔面试时经常遇到的跨域问题 到底什么是跨域呢?

出现跨域这个词 首先是因为浏览器的同源限制策略。 浏览器规定只有 端口,协议 ,域名都一致才属于同源,可以互相访问

下图给出了不同源的结果


URL                                      	结果	原因
http://store.company.com/dir2/other.html	同源	只有路径不同
http://store.company.com/dir/inner/another.html	同源	只有路径不同
https://store.company.com/secure.html	         失败	协议不同
http://store.company.com:81/dir/etc.html	失败	端口不同 ( http:// 默认端口是80)
http://news.company.com/dir/other.html   	失败	主机不同
复制代码

小朋友 你是否有很多问号 跨域原来是浏览器的锅 为什么一定要限制同源才能互相访问呢?

如果没有限制同源访问 一些钓鱼网站可以读取你的cookie信息 伪造身份向其他浏览器发送请求 也就是我们常说的csrf攻击。

解决跨域的常用方法

JSONP

JSONP这个名字听起来好像和JSON有很大关系 其实并没有。实质上就是通过动态插入script标签 通过src路径去访问跨域的地址 参数上带上要执行的回调函数 然后通过解析响应得到想要的信息。

这种做法有限制就是只能发送GET请求。那img标签的src可以用吗?别问,问就是不行,因为只有script标签可以解析响应。

实现如下: 具体可以参考我的另一篇文章JSONP是什么

  let functionName = 'tina'+parseInt(Math.random()*100000,10);
//给调用函数创建一个随机的函数名
  script.src = 'http://u.com:8002/pay?callback=' + functionName; 
//u.com为响应网站的域名,8002为响应网站的端口号

window[callback] = function(result){ //定义函数
            if(result === 'success'){
                amount.innerText = amount.innerText - 1;
            }
        }
        script.onload = function(e){
            e.currentTarget.remove();//执行结束删除script标签
            delete window[callback]; //执行结束删除函数
        }
        script.onerror = function (e) {
            alert('fail');
            e.currentTarget.remove();//执行结束删除script标签
            delete window[callback];//执行结束删除函数
        }
    })
复制代码

window.postMessage

MDN上的定义window.postMessage

从广义上讲,一个窗口可以获得对另一个窗口的引用(比如 targetWindow = window.opener),然后在窗口上调用 targetWindow.postMessage() 方法分发一个 MessageEvent 消息。接收消息的窗口可以根据需要自由处理此事件。传递给 window.postMessage() 的参数(比如 message )将通过消息事件对象暴露给接收消息的窗口。

语法

otherWindow.postMessage(message, targetOrigin, [transfer]);
// targetOrigin属性规定了哪些窗口可以接收到消息

复制代码
/*
 * A窗口的域名是<http://example.com:8080>,以下是A窗口的script标签下的代码:
 */

var popup = window.open(...popup details...);

// 如果弹出框没有被阻止且加载完成

// 这行语句没有发送信息出去,即使假设当前页面没有改变location(因为targetOrigin设置不对)
popup.postMessage("The user is 'bob' and the password is 'secret'",
                  "https://secure.example.net");

// 假设当前页面没有改变location,这条语句会成功添加message到发送队列中去(targetOrigin设置对了)
popup.postMessage("hello there!", "http://example.org");

function receiveMessage(event)
{
  // 我们能相信信息的发送者吗?  (也许这个发送者和我们最初打开的不是同一个页面).
  if (event.origin !== "http://example.org")
    return;

  // event.source 是我们通过window.open打开的弹出页面 popup
  // event.data 是 popup发送给当前页面的消息 "hi there yourself!  the secret response is: rheeeeet!"
}
window.addEventListener("message", receiveMessage, false);


/*
 * 弹出页 popup 域名是<http://example.org>,以下是script标签中的代码:
 */

//当A页面postMessage被调用后,这个function被addEventListenner调用
function receiveMessage(event)
{
  // 我们能信任信息来源吗?
  if (event.origin !== "http://example.com:8080")
    return;

  // event.source 就当前弹出页的来源页面
  // event.data 是 "hello there!"

  // 假设你已经验证了所受到信息的origin (任何时候你都应该这样做), 一个很方便的方式就是把event.source
  // 作为回信的对象,并且把event.origin作为targetOrigin
  event.source.postMessage("hi there yourself!  the secret response " +
                           "is: rheeeeet!",
                           event.origin);
}

window.addEventListener("message", receiveMessage, false);
复制代码

postMessage在使用的过程中如果没有指定origin去验证发消息的窗口是否合法是很危险的 同时也需要验证接收到的内容是否对站点是否构成攻击

document.domain

这种方式只适合主域名相同,但子域名不同的iframe跨域。比如主域名是http://crosdomain.com:9099,子域名是http://child.crosdomain.com:9099,这种情况下给两个页面指定一下document.domain即document.domain = crosdomain.com就可以访问各自的window对象了。

CROS

CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing)这里可以参考阮一峰老师的文章

cros跨域请求分为简单请求和非简单请求

(1) 请求方法是以下三种方法之一:

HEAD
GET
POST
(2)HTTP的头信息不超出以下几种字段:

Accept
Accept-Language
Content-Language
Last-Event-ID
Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain`
复制代码

对于简单的请求只需要在请求头添加上origin 标示自己身份 后端添加上Access-Control-Allow-Origin标示同意这个身份访问;

如果需要发送cookie 则需要前后端都设置Access-Control-Allow-Credentials

对于非简单请求 会先发送一个预请求 检查请求头成功后返回204 然后再触发第二次请求

Nginx反向代理

前端请求以本地路径路径进行访问 发送请求到Nginx服务器上 再以真实的域名广播到请求服务器上 这一层代理之后就不存在跨域问题了 。




原文地址:访问原文地址
快照地址: 访问文章快照