How to identify if a webpage is being loaded inside an iframe or directly into the browser window?
我正在写一个基于iFrame的Facebook应用程序。现在我想使用相同的HTML页面来呈现普通的网站以及Facebook中的画布页面。我想知道我是否可以确定页面是在iframe中加载的,还是直接在浏览器中加载的?
由于相同的源站策略,浏览器可以阻止对
1 2 3 4 5 6 7 | function inIframe () { try { return window.self !== window.top; } catch (e) { return true; } } |
当在与父级相同的iframe中,
1 2 3 | window.frameElement ? 'embedded in iframe or object' : 'not embedded or cross-origin' |
这是一个HTML标准,在所有现代浏览器中都有基本支持。
罗博格是对的,但我想加一个旁注。
在IE7/IE8中,当微软在浏览器中添加标签时,他们破坏了一个东西,如果你不小心的话,这会给你的JS造成严重破坏。
想象一下这个页面布局:
1 2 3 4 | MainPage.html IframedPage1.html (named"foo") IframedPage2.html (named"bar") IframedPage3.html (named"baz") |
现在,在"baz"帧中,您单击一个链接(没有目标,在"baz"帧中加载),它工作正常。
如果加载的页面,让我们称之为special.html,使用JS检查"it"是否具有名为"bar"的父帧,它将返回true(预期)。
现在假设special.html页面在加载时,检查父框架(是否存在及其名称,如果它是"bar",它会将自己重新加载到bar框架中。例如
1 2 3 | if(window.parent && window.parent.name == 'bar'){ window.parent.location = self.location; } |
到现在为止,一直都还不错。现在虫子来了。
比如说,不要像平常一样单击原始链接,在"baz"框中加载special.html页面,而是用鼠标中键单击它或选择在新选项卡中打开它。
当加载新选项卡时(根本没有父帧!)IE将进入一个无休止的页面加载循环!因为ie"复制"了javascript中的框架结构,所以新的选项卡确实有一个父项,而父项有一个名称"bar"。
好消息是,检查:
1 2 3 | if(self == top){ //this returns true! } |
在这个新的选项卡中,返回的是真的,因此您可以测试这个奇怪的条件。
在火狐6.0扩展(addon sdk 1.0)的内容脚本中,接受的答案对我无效:火狐在每个顶级窗口和所有iframe中执行内容脚本。
在内容脚本中,我得到以下结果:
1 2 | (window !== window.top) : false (window.self !== window.top) : true |
这个输出的奇怪之处在于它总是相同的,不管代码是在iframe还是顶层窗口中运行。
另一方面,谷歌Chrome似乎只在顶层窗口中执行一次我的内容脚本,所以上面的内容根本不起作用。
在这两种浏览器的内容脚本中,我最后都能使用的是:
1 | console.log(window.frames.length + ':' + parent.frames.length); |
如果没有iframes,它将在包含一个框架的顶层窗口中打印
这允许我的扩展在两个浏览器中确定是否存在任何iframe,另外在firefox中,如果它运行在其中一个iframe中。
我不确定这个例子在旧的Web浏览器中是如何工作的,但是我在IE、Firefox和Chrome中使用这个例子,没有和问题:
1 | var iFrameDetection = (window === window.parent) ? false : true; |
我用这个:
1 | var isIframe = (self.frameElement && (self.frameElement+"").indexOf("HTMLIFrameElement") > -1); |
使用此javascript函数作为如何完成此操作的示例。
4最适合现在使用的旧浏览器断帧脚本
其他的解决方案对我来说不起作用。此功能适用于所有浏览器:
防止点击劫持的一种方法是在每一个不应该被框架化的页面中包含一个"框架破坏器"脚本。以下方法将阻止网页被框架化,即使在不支持x-frame-options-header的传统浏览器中也是如此。
在document head元素中,添加以下内容:
1 | <style id="antiClickjack">body{display:none !important;}</style> |
首先将ID应用于样式元素本身:
4这样,所有内容都可以在文档头中,并且您的API中只需要一个方法/taglib。
参考:https://www.codemagi.com/blog/post/194
实际上我曾经检查window.parent,它对我很有用,但是最近window是一个循环对象,总是有一个parent key,iframe或者没有iframe。
正如评论所暗示的,与window.parent进行比较很困难。如果iframe与父网站完全相同,则不确定这是否有效。
1 | window === window.parent; |
由于您是在Facebook应用程序的上下文中询问的,所以您可能需要考虑在发出初始请求时在服务器上检测到这一点。如果从iframe调用,facebook将传递一组查询字符串数据,包括fb-sig-u用户密钥。
由于您可能需要在应用程序中检查和使用这些数据,因此可以使用它来确定要呈现的适当上下文。
这最终成为我最简单的解决方案。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | <p id="demofsdfsdfs"> </p> if(window.self !== window.top) { //run this code if in an iframe document.getElementById("demofsdfsdfs").innerHTML ="in frame"; }else{ //run code if not in an iframe document.getElementById("demofsdfsdfs").innerHTML ="no frame"; } |
如果你想知道用户是否正在通过Facebook页面选项卡或画布访问你的应用,请检查签名请求。如果你不明白,可能用户没有从Facebook访问。确保确认已签名的请求字段结构和字段内容。
通过php sdk,您可以获得如下签名请求:
1 | $signed_request = $facebook->getSignedRequest(); |
您可以在此处阅读有关签名请求的更多信息:
https://developers.facebook.com/docs/reference/php/facebook-getsignedRequest/
这里:
https://developers.facebook.com/docs/reference/login/signed-request/
这是一段我用过几次的古老代码:
1 2 3 | if (parent.location.href == self.location.href) { window.location.href = 'https://www.facebook.com/pagename?v=app_1357902468'; } |
在每个页面中编写此javascript
1 2 | if (self == top) { window.location ="Home.aspx"; } |
然后它会自动重定向到主页。
1 | if (window.frames.length != parent.frames.length) { page loaded in iframe } |
但只有当在iframe中加载您的页面和页面中的iframe数量不同时。在您的页面中不使用iframe,以100%保证此代码的结果。