回顾

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

使用ETag进行session的降级的更多相关文章

  1. cookie/session在nodes中的实战

    cookie 和 session 众所周知,HTTP 是一个无状态协议,所以客户端每次发出请求时,下一次请求无法得知上一次请求所包含的状态数据,如何能把一个用户的状态数据关联起来呢? 比如在淘宝的某个 ...

  2. Redis安装及实现session共享

    一.Redis介绍 1.redis是key-value的存储系统,属于非关系型数据库 2.特点:支持数据持久化,可以让数据在内存中保存到磁盘里(memcached:数据存在内存里,如果服务重启,数据会 ...

  3. web浏览器下的缓存 - Etag

    设置浏览器缓存的几种方法: Last-Modified : 服务器上文件的最后修改时间 Etag  : 文件标识 Expiers : 本地缓存目录中文件过期的时间 ( 由服务器指定具体的时间 ) Ca ...

  4. 用PHP操作http中Etag、lastModified和Expires标签

    http://blog.hehehehehe.cn/a/10994.htm 客户端通过浏览器发出第一次请求某一个URL时,根据 HTTP 协议的规定,浏览器会向服务器传送报头(Http Request ...

  5. SDP (Session Description Protocol)

    SDP的描述网络上一大堆中文的,可以看看RFC2327 SDP 信息是文本信息,采用 UTF-8 编 码中的 ISO 10646 字符集.SDP 会话描述如下:(标注 * 符号的表示可选字段):  v ...

  6. HTTP协议----ETag

    Etag是URL的Entity Tag,用于标示URL对象是否改变,区分不同语言和Session等等.具体内部含义是服务器控制的,就像Cookie那样. 服务器首先产生ETag,服务器可在稍后使用它来 ...

  7. HTTP协议缓存策略深入详解之ETAG妙用

    Etag是什么: Etag 是URL的Entity Tag,用于标示URL对象是否改变,区分不同语言和Session等等.具体内部含义是使服务器控制的,就像Cookie那样. HTTP协议规格说明定义 ...

  8. Last-Modified和ETag以及Apache和Nginx中的配置

    1) 什么是”Last-Modified”? 在浏览器第一次请求某一个URL时,服务器端的返回状态会是200,内容是你请求的资源,同时有一个Last-Modified的属性标记此文件在服务期端最后被修 ...

  9. sonar + jacoco + mockMvc 模拟session 用户登录 配合SpringSecurity 权限 快速测试代码覆盖率.

    遇到mock 测试简直就是神器,特别是要做代码覆盖率,直接测试controller就好了,缺点,虽然可以回滚事务,但是依赖数据库数据,解决,根据SpringBoot ,再建立一个专门跑单元测试的数据库 ...

随机推荐

  1. 百度推出新技术 MIP,网页加载更快,广告呢?

    我们在2016年年初推出了MIP,帮助移动页面加速(原理).内测数据表明,MIP页面在1s内加载完成.现在已经有十多家网站加入MIP项目,有更多的网站正在加入中.在我们收到的反馈中,大部分都提到了广告 ...

  2. H5单页面手势滑屏切换原理

    H5单页面手势滑屏切换是采用HTML5 触摸事件(Touch) 和 CSS3动画(Transform,Transition)来实现的,效果图如下所示,本文简单说一下其实现原理和主要思路. 1.实现原理 ...

  3. logstash file输入,无输出原因与解决办法

    1.现象 很多同学在用logstash input 为file的时候,经常会出现如下问题:配置文件无误,logstash有时一直停留在等待输入的界面 2.解释 logstash作为日志分析的管道,在实 ...

  4. 设置tomcat远程debug

    查看端口占用情况命令: netstat -tunlp |grep 8000 tomcat 启动远程debug: startup.sh 中的最后一行 exec "$PRGDIR"/& ...

  5. 声音分贝的概念,dBSPL.dBm,dBu,dBV,dBFS

    需要做个音频的PPM表,看着一堆的音频术语真是懵了,苦苦在网上扒了几天的文档,终于有了点收获,下面关于声音的分贝做个总结. 分贝 Decibel 分贝(dB)是一个对数单位(logarithmic u ...

  6. 设计模式C#合集--抽象工厂模式

    抽象工厂,名字就告诉你是抽象的了.上代码. public interface BMW { public void Drive(); } public class BMW730 : BMW { publ ...

  7. ie6 ie7 ie8 ie9兼容问题终极解决方案

    放下包袱,解决低版本兼容问题   这是一个老生常谈的问题,自然解决这个问题的方案也比较多,下面整理了一些解决方法: 1.强制使用高版本渲染模式. 强制使用Edge模式来解析网页代码 <meta ...

  8. 【干货分享】流程DEMO-人员调动流程

    流程名: 调动 流程相关文件: 流程包.xml 流程说明: 直接导入流程包文件,即可使用本流程 表单:  流程:  图片:3.png DEMO包下载: http://files.cnblogs.com ...

  9. 【搬砖】安卓入门(1)- Java开发入门

    01.01_计算机基础知识(计算机概述)(了解) A:什么是计算机?计算机在生活中的应用举例 计算机(Computer)全称:电子计算机,俗称电脑.是一种能够按照程序运行,自动.高速处理海量数据的现代 ...

  10. 我的MYSQL学习心得(五) 运算符

    我的MYSQL学习心得(五) 运算符 我的MYSQL学习心得(一) 简单语法 我的MYSQL学习心得(二) 数据类型宽度 我的MYSQL学习心得(三) 查看字段长度 我的MYSQL学习心得(四) 数据 ...