今天继续来聊 Cookie
,Chrome
已经在 1.4
号开启了三方 Cookie
的 1%
禁用灰度:
不少小伙伴反馈已经命中了这个灰度,因为时间比较急,很多网站来不及改造,很多网站的正常功能在这个灰度策略下受到了影响。
在前面的文章中我们提到,对于一些还没来得及改造完的网站,Chrome
提供了一种便捷的方式来让命中灰度的用户手动关闭这个策略:
这个开关点击后可以允许指定域名继续使用三方 Cookie
,但是这个期限只有 90 天。
所以,如果大家的网站最近没有时间进行这些改造,大家可以在运行时来提示用户手动关闭三方 Cookie
的禁用策略。
那么问题来了,并不是所有用户都命中了这个策略,当前只有 1%
,我们可能给所有的用户都添加这个提示,所以我们如何在运行时检测用户是否命中了三方 Cookie
的灰度策略呢?
ini
function checkCookie(){
var cookieEnabled = navigator.cookieEnabled;
if (!cookieEnabled){
document.cookie = "testcookie";
cookieEnabled = document.cookie.indexOf("testcookie")!=-1;
}
return cookieEnabled || showCookieFail();
}
function showCookieFail(){
// do something here
}
checkCookie();
上面的代码片段可用于检查 Cookie
是否启用,但是对三方 Cookie
的检查就无能为力了,三方 Cookie
禁用的情况下还是会返回 true
。
我能想到的并且一直有效的方法就是添加一个外部(三方)的 iFrame
,让它来检测 iFrame
内部是否可以访问到 Cookie
,并且会将 Cookie
的可用状态通知给父应用。
虽然这听起来挺奇怪的,我们好像无法直接通过 iFrame
调用父页面的功能。但是我们可以使用 Message Event
来进行父子应用之间的通信,通过这个我们可以基于 URL
向其他浏览器发送消息,在我们现在这种情况下,我们可以从 iFrame
向可能在不同域上的父应用发送消息。
首先,我们在 iFrame
内添加一个立即执行函数。在这个函数中,我们添加一个消息事件监听器,这个监听器会在从父级应用程序调用时触发。当被调用时,它首先会验证请求,然后调用 checkCookiesEnable
函数来检查 Cookie
的状态并返回结果。然后,我们通过 parent.postMessage()
方法向父应用发送一条消息;在 iFrame
中,parent
是一个隐含的对象。
xml
<!doctype html>
<html>
<head>
</head>
<body>
<script>
const checkCookiesEnable = () => {
let isCookieEnabled = (window.navigator.cookieEnabled) ? true : false;
if (typeof window.navigator.cookieEnabled == "undefined" && !isCookieEnabled) {
// 尝试设置一个测试cookie
document.cookie = "testcookie";
// 检查cookie是否已设置
isCookieEnabled = (document.cookie.indexOf("testcookie") != -1) ? true : false;
}
return isCookieEnabled;
}
// 监听消息事件,响应从父窗口传来的消息
(function () {
window.addEventListener('message', event => {
try {
let data = JSON.parse(event.data)
if (data['test'] !== 'cookie')
return
let result = checkCookiesEnable();
// 将结果通过消息事件发送到父窗口
parent.postMessage(JSON.stringify({
'result': result
}), event.origin);
}
catch (e) {
console.error(e)
}
});
})();
</script>
</body>
</html>
在这里,我们将添加一个消息事件处理程序,然后在插入任何第三方脚本之前插入我们的 iFrame
。一旦 iFrame
加载完毕,我们将通过 frame.contentWindow
对象向我们的 iFrame
发送 postMessage
,使用 "*"
允许 postMessage
任何来源(不同的域)。
然后,iFrame
内部的函数检查iFrame
的 Cookie
状态并发送一个消息,该消息被我们的 messagehandler
拦截。检查消息是否由 iFrame
发送,事件现在将保存来自 iFrame
内的 checkCookieEnable
函数结果的响应。
下面是一个示例函数,它接受iframeUri和一个回调函数,在收到结果后将被调用。
ini
const cookieTest = (iFrameUri, callBack) => {
let messageHandler = (event) => {
// 在这里检查受信任的来源
const data = JSON.parse(event.data);
callBack(data['result']);
window.removeEventListener('message', messageHandler);
document.body.removeChild(frame);
};
// 监听消息事件,响应从 iframe 窗口传来的消息
window.addEventListener('message', messageHandler);
// 创建并添加一个隐藏的 iframe 元素
const frame = document.createElement('iframe');
frame.src = iFrameUri;
frame.sandbox = "allow-scripts allow-same-origin";
frame.style = `display:none`;
frame.onload = (e) => {
// 向 iframe 发送一个消息,请求检查 cookie 的情况
frame.contentWindow.postMessage(JSON.stringify({ 'test': 'cookie' }), '*');
};
document.body.appendChild(frame);
};
export default cookieTest;
你可以直接把上面的代码片段放入你的网站中,并提供一个回调函数来为用户呈现适当的消息。
现在,我们可以成功地在运行时检测到用户的第三方 Cookie
是否已启用了!