V2EX = way to explore
V2EX 是一个关于分享和探索的地方
Sign Up Now
For Existing Member  Sign In
爱意满满的作品展示区。
zjcqoo

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

  •  
  •   zjcqoo · Mar 30, 2018 · 2890 views
    This topic created in 2956 days ago, the information mentioned may be changed or developed.

    前言

    由于 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  
       Mar 30, 2018
    通过 iframe、img、script 等标签跨域加载内容时,发出的请求的时候本就是会携带 cookie 的。
    不是很理解这个会话 ID 的用途是什么。
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Solana   ·   2317 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 101ms · UTC 05:16 · PVG 13:16 · LAX 22:16 · JFK 01:16
    ♥ Do have faith in what you're doing.