V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
爱意满满的作品展示区。
zjcqoo
V2EX  ›  分享创造

[分享] 浏览器级的会话 ID,非常轻量

  •  
  •   zjcqoo · 2018-03-30 15:26:49 +08:00 · 2517 次点击
    这是一个创建于 2441 天前的主题,其中的信息可能已经有所发展或是发生改变。

    前言

    由于 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 后,同时记录到 etagcookie 里,这两个字段也是具有持久性的,用户下次请求时会带上它们。

    服务器收到请求时,会优先使用 etagcookie 的记录;如果没有,才生成新串。这样就能双重保险了。

    实现

    这里使用 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,或者更好的思路,可以讨论交流。

    M4ster
        1
    M4ster  
       2018-03-30 18:41:19 +08:00
    通过 iframe、img、script 等标签跨域加载内容时,发出的请求的时候本就是会携带 cookie 的。
    不是很理解这个会话 ID 的用途是什么。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1163 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 21ms · UTC 23:09 · PVG 07:09 · LAX 15:09 · JFK 18:09
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.