由于 cookie 只能跨子域,因此根域不同的网站之间无法共享唯一 ID。要解决这个问题,通常嵌入第三方 iframe 来实现。
但是 iframe 开销比较大,于是这里做了一个精简版本,仅通过一个 JS 即可获得浏览器级的 ID。
URL:https://tool.ns6.top/cache_id
该 URL 返回一个 JS 资源,格式类似于 JSONP。
回调名暂时固定为 __cacheid_cb
。回调内容为一个随机串,作为 ID。
<script>
self.__cacheid_cb = function(id) {
console.log(id);
};
</script>
<!-- 也可以动态引入 -->
<script src="https://tool.ns6.top/cache_id"></script>
https://codepen.io/anon/pen/MVVJGv?editors=1000
https://jsfiddle.net/23xvqfd1/3/
在不同的网站上,能获得相同的 ID:
用户首次加载 https://tool.ns6.top/cache_id
时,服务端返回一个随机串,并设置强缓存:
Cache-Control: max-age=315360000
用户后续加载该 URL 时,即可直接从缓存中读取,因此获得的仍是之前的内容!只要用户不清空缓存,获取的内容始终是相同的。
由于浏览器缓存没有同源策略限制,因此各个网站上都可共享这份数据。
当然,假如用户刷新页面,那么本地缓存会失效,这样后端会生成新的 ID。
为了解决这个问题,服务器生成 ID 后,同时记录到 etag
和 cookie
里,这两个字段也是具有持久性的,用户下次请求时会带上它们。
服务器收到请求时,会优先使用 etag
和 cookie
的记录;如果没有,才生成新串。这样就能双重保险了。
这里使用 nginx-lua 实现:
location = /cache_id {
default_type application/javascript;
add_header Cache-Control "max-age=315360000, private";
content_by_lua "
local etag = ngx.var.http_If_None_Match
local cookie = ngx.var.cookie_uid
local cookieChanged
if etag == nil then
if cookie == nil then
cookie = string.format(
'%08x%08x',
math.random(0, 0xffffffff),
math.random(0, 0xffffffff)
)
cookieChanged = true
end
etag = cookie
else
if cookie ~= etag then
cookie = etag
cookieChanged = true
end
end
ngx.header['ETag'] = etag;
if cookieChanged then
ngx.header['Set-Cookie'] = 'uid=' .. etag ..
'; expires=Thu, 30 Jan 2031 08:00:00 GMT'
end
ngx.say('__cacheid_cb(\"'.. etag .. '\")');
";
}
需要注意的是,Cache-Control
里有一个 private
标志,主要是为了避免 CDN 等代理缓存该资源,导致某个地区的用户出现同样的值。
当然,如果有什么 BUG,或者更好的思路,可以讨论交流。
1
M4ster 2018-03-30 18:41:19 +08:00
通过 iframe、img、script 等标签跨域加载内容时,发出的请求的时候本就是会携带 cookie 的。
不是很理解这个会话 ID 的用途是什么。 |