Safari 中的 Rail、ajax 和 iframe
Rail, ajax and iframe in Safari
在我们的应用程序中,我们有一些与 ajax 一起使用的元素。
我们允许用户将部分应用程序嵌入到 iframe 中。
在 Chrome 和 Mozilla 中一切正常。在 Safari 中我们收到 422 错误,服务器日志如下所示:
2015-07-15T08:26:06.818885+00:00 app[web.1]: Completed 422 Unprocessable Entity in 4ms
2015-07-15T08:26:06.815411+00:00 app[web.1]: Can't verify CSRF token authenticity
2015-07-15T08:26:06.823389+00:00 app[web.1]: ActionController::InvalidAuthenticityToken (ActionController::InvalidAuthenticityToken):
我们发现如果我们直接访问 iframe url 然后访问包含 iframe 的页面它工作正常,这可能表明它与 cookie 有关。
我试过了this solution,但还是有这个问题。
最后我发现 Safari 不允许在 iframe 中保留 cookie。
也就是说,如果你需要使用cookies,你需要做一些事情。
我找到的解决方案是这样的:
- 在 iframe 代码中检查
session[:safari_cookie_fixed]
是否存在。如果不是,请呈现一个代码,该代码将使用 postMessage
与父 window 进行通信,并让他知道我们需要重定向。
- 加载 iframe 时,父级 window 通过
postMessage
向 iframe 发送信号。在 cookie 不存在的情况下呈现的侦听器向父级发送信号以执行重定向,父级重定向到 iframe 域中的 /set-cookie
页面,添加其当前 url作为查询字符串参数。
set_cookie
操作保存 cookie safari_cookie_fixed
并重定向回父页面(其 url 在查询字符串中可用)
当然,此解决方案需要在父页面中添加一些 js 代码,但是当您为用户提供 iframe html 代码时,您也可以包含 js。
Safari 不允许(默认情况下)在 iFrame 中设置 cookie,除非用户已经访问过该站点。想象一下这段代码:
# a.com
<iframe src="b.com/iframe">
如果您直接访问 b.com
(任何页面)并且它设置了一个 cookie,那么当您访问 a.com
并且加载到 b.com
的 iFrame 时,Safari 将发送该 cookie。
但是,如果您在没有先访问 b.com
的情况下访问 a.com
,则 Safari 将忽略 b.com/iframe
尝试设置的任何 cookie。
名为 "A solution for Safari" 的部分中的 solution is described here。
- 检测是否支持cookies。
- 如果没有,在页面上放置一个叠加层说,"This site requires cookies. Please click here to continue."
- 当用户点击 link/button 时,打开一个弹出窗口,它设置一个 cookie 并自行关闭。在桌面上,您可以使弹出窗口相当小且无害。在移动设备上它更难看,因为你不能让弹出窗口小于全屏。但是,无论哪种方式,弹出窗口仅在屏幕上显示不到一秒钟。
- iframe 会自行重新加载,现在可以使用 cookie。
在我们的应用程序中,我们有一些与 ajax 一起使用的元素。 我们允许用户将部分应用程序嵌入到 iframe 中。
在 Chrome 和 Mozilla 中一切正常。在 Safari 中我们收到 422 错误,服务器日志如下所示:
2015-07-15T08:26:06.818885+00:00 app[web.1]: Completed 422 Unprocessable Entity in 4ms
2015-07-15T08:26:06.815411+00:00 app[web.1]: Can't verify CSRF token authenticity
2015-07-15T08:26:06.823389+00:00 app[web.1]: ActionController::InvalidAuthenticityToken (ActionController::InvalidAuthenticityToken):
我们发现如果我们直接访问 iframe url 然后访问包含 iframe 的页面它工作正常,这可能表明它与 cookie 有关。
我试过了this solution,但还是有这个问题。
最后我发现 Safari 不允许在 iframe 中保留 cookie。
也就是说,如果你需要使用cookies,你需要做一些事情。
我找到的解决方案是这样的:
- 在 iframe 代码中检查
session[:safari_cookie_fixed]
是否存在。如果不是,请呈现一个代码,该代码将使用postMessage
与父 window 进行通信,并让他知道我们需要重定向。 - 加载 iframe 时,父级 window 通过
postMessage
向 iframe 发送信号。在 cookie 不存在的情况下呈现的侦听器向父级发送信号以执行重定向,父级重定向到 iframe 域中的/set-cookie
页面,添加其当前 url作为查询字符串参数。 set_cookie
操作保存 cookiesafari_cookie_fixed
并重定向回父页面(其 url 在查询字符串中可用)
当然,此解决方案需要在父页面中添加一些 js 代码,但是当您为用户提供 iframe html 代码时,您也可以包含 js。
Safari 不允许(默认情况下)在 iFrame 中设置 cookie,除非用户已经访问过该站点。想象一下这段代码:
# a.com
<iframe src="b.com/iframe">
如果您直接访问 b.com
(任何页面)并且它设置了一个 cookie,那么当您访问 a.com
并且加载到 b.com
的 iFrame 时,Safari 将发送该 cookie。
但是,如果您在没有先访问 b.com
的情况下访问 a.com
,则 Safari 将忽略 b.com/iframe
尝试设置的任何 cookie。
名为 "A solution for Safari" 的部分中的 solution is described here。
- 检测是否支持cookies。
- 如果没有,在页面上放置一个叠加层说,"This site requires cookies. Please click here to continue."
- 当用户点击 link/button 时,打开一个弹出窗口,它设置一个 cookie 并自行关闭。在桌面上,您可以使弹出窗口相当小且无害。在移动设备上它更难看,因为你不能让弹出窗口小于全屏。但是,无论哪种方式,弹出窗口仅在屏幕上显示不到一秒钟。
- iframe 会自行重新加载,现在可以使用 cookie。