前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >使用ETag进行session的降级

使用ETag进行session的降级

作者头像
欲休
发布2018-03-15 14:12:05
1.1K0
发布2018-03-15 14:12:05
举报
文章被收录于专栏:前端杂货铺前端杂货铺

回顾

在web后台开发中我们经常需要存储一些变量到session中进行暂存,最为特殊的就是“购物车”,由于http的无状态特性,因此我们需要在客户端打上一个标记,唯一的标示客户端并和服务端session一一对应,因此就有了通过cookie和url进行存储或传递这个标示--sessionID。 sessionID是一个长的字符串,它往往默认通过cookie来保存,这个session并不持久化到硬盘而是暂存到内存中,每次请求时都会在head中带上这个包含sessionID的cookie,服务端可以根据该id标示出客户端,从而访问服务器的一片内存区;另一种通过url方式传递sessionID,当cookie在客户端被禁用时,服务端会将生成的sessionID加入到url,以此完成sessionID的传输,又称url回写。但是url回写会有明显的安全漏洞,当该网站被xss注入时,攻击者就可以通过窃取的sessionID访问服务端的隐私数据。

惊喜

无意中,在snoopyXDY的文章中看到了用ETag进行兼容,大喜,并感慨于该方法的奇妙。(之前遇到过ETag在服务器集群中同步的问题,原因是在服务端生成ETag的方式不妥,最终解决方案就是针对请求文件的内容进行hash并base64编码,这样在服务端同步的前提下,请求任意服务器都会返回相同的ETag)在此处实现中,则是异步请求一个js文件,该文件会根据客户端的相关头部设置或获取session,并且在服务端触发客户端的相关事件,完成数据的传递。 github地址:https://github.com/royalrover/ETag-Session

揭秘

首先,我们访问登陆页面login.html,在页面底部的script中,异步加载一个名称为‘eTag.js’的文件,这个文件并不是静态的,而是由服务端根据客户端传递的参数进行相应处理:如果客户端的request头部有‘if-none-match’字段,则会在内存中查看是否有该字段对应的value(服务端用hash进行存储各个客户的的session),并将该value值序列化,同时触发客户端的‘etag-ready’事件,并将序列化的value作为值传入。 在这里的实现中,有可能会存在浏览器对动态文件‘eTag.js’的缓存,为了避免‘eTag.js’的准确和实时,因此需要设置‘cache-control’头部。

代码语言:javascript
复制
router.get('/_eTag_.js',function * (next){
  var ctx = this;
  var etag = ctx.header['if-none-match'];
  var cache;

  if(!etag) {
      etag = new Date().getTime() + '__etag';
  }
//    console.log(session)
  if(session[etag]) {
    cache = session[etag];
  }else {
    cache = {
        etag: etag
    };
  }
  cache = JSON.stringify(cache);
  ctx.set('ETag',etag);
  ctx.set('cache-control','no-cache');
  ctx.set('content-type','application/javascript');
//    ctx.body = 'window._session = '+ cache + ';';
  ctx.body = 'd.do("etag-ready",'+ cache +')';
//    console.log('cache: '+cache)
  yield *next;
})

在处理post请求时,node并不会解析body,因此需要我们自己来搞定,可以通过模块,也可以简单的通过订阅事件,在这里我是简单的用node原生的request对象进行侦听。 为了避免js阻塞渲染,采用异步加载的方式获取,但这也会造成从服务端获取的数据不能及时被客户端处理和渲染,为了解决这个问题,此处采用了重量级应用必备的解决方案-Bigpipe,有服务端出发客户端订阅的事件,一旦服务端去到session数据,则触发'etag-ready'事件,并在客户端进行逻辑处理和渲染。 客户端的逻辑如下:

代码语言:javascript
复制
function $(n){
        return document.querySelectorAll(n);
    }

    function asyncLoad(src) {
        var s = document.createElement('script');
        s.src = src;
        s.async = true;
        document.body.appendChild(s);
    }

    function DO(){
        this.cbs = [];
    }
    DO.prototype.on = function(k,cb){
        if(!this.cbs[k]) {
            this.cbs[k] = [];
        }
        this.cbs[k].push(cb);
    };
    DO.prototype.do = function(k,data){
        if(!this.cbs[k])
        return;
        var cbs = this.cbs[k],len = cbs.length;
        for(var i=0;i<len;i++){
            cbs[i].call(this,data);
        }
    };

    asyncLoad('/_eTag_.js');
    var d = new DO();
    d.on('etag-ready',function(_session){
        console.log('etag-ready...');
        console.log(_session);
        if(_session && _session.etag && !_session.usrname) {
            $('[name=etag]')[0].value = _session.etag;
        }else {
            $('[name=usrname]')[0].value = _session.usrname;
            $('[name=pwd]')[0].value = _session.pwd;
            $('[name=etag]')[0].value = _session.etag;
        }
    })

总结

使用ETag方式来hack兼容性是非常棒的,几乎所有的服务器都实现了这个机制(HTTP1.1规范),因此兼容性不是问题。由于使用ETag加载的文件的元数据都保存在浏览器的缓存中,因此安全性是没法与存储在内存中的cookie方式相比的,而且如果清空浏览器缓存,那么客户端则丢失sessionID,没法在使用session。因此这种方式也仅仅作为cookie被禁用的一种候补方案,不推荐大规模使用。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 回顾
  • 惊喜
  • 揭秘
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档