zoukankan      html  css  js  c++  java
  • 使用ETag进行session的降级

    回顾

    在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’头部。

    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'事件,并在客户端进行逻辑处理和渲染。
    客户端的逻辑如下:

    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被禁用的一种候补方案,不推荐大规模使用。

  • 相关阅读:
    你真的了解HTML吗?–雅虎面试题
    移动端meta 解释
    PLSQL导入导出表的正确步骤
    Linux命令学习之xargs命令
    python搭建简易Web Server
    python小项目练习之转换像素图片为字符图
    向量空间搜索引擎基本理论
    Elasticsearch 常用基本查询
    Elasticsearch配置参数介绍
    JavaWeb温习之防止表单重复提交
  • 原文地址:https://www.cnblogs.com/accordion/p/4625267.html
Copyright © 2011-2022 走看看