回顾

在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. 一个粗心的Bug,JSON格式不规范导致AJAX错误

    一.事件回放  今天工作时碰到了一个奇怪的问题,这个问题很早很早以前也碰到过,不过没想到过这么久了竟然又栽在这里. 当时正在联调一个项目,由于后端没有提供数据接口,于是我直接本地建立了一个 json ...

  2. SQL Server内存遭遇操作系统进程压榨案例

    场景: 最近一台DB服务器偶尔出现CPU报警,我的邮件报警阈(请读yù)值设置的是15%,开始时没当回事,以为是有什么统计类的查询,后来越来越频繁. 探索: 我决定来查一下,究竟是什么在作怪,我排查的 ...

  3. 清空Github上某个文件的历史版本

    title: 清空Github上某个文件的历史版本 author: 青南 date: 2015-01-08 16:04:53 categories: [经验] tags: [Github,histor ...

  4. iOS逆向工程之Reveal工具的安装、配置与使用

    今天博客内容比较简单,不过还是蛮重要的.经常有小伙伴在QQ上私下问我,说博客中是如何使用Reveal查看AppStore中下载应用的UI层级的,那么就在今天这篇博客中作为一个主题来统一的介绍一下吧.虽 ...

  5. bzoj3037--贪心

    题目大意: applepi手里有一本书<创世纪>,里面记录了这样一个故事--上帝手中有着N 种被称作"世界元素"的东西,现在他要把它们中的一部分投放到一个新的空间中去以 ...

  6. Android Studio 编译单个module

    前期自己要把gradle环境变量配置好 在Terminal中gradle命令行编译apk 输入gradle assembleRelease 会编译全部module编译单个modulecd ./xiru ...

  7. Android的Kotlin秘方(I):OnGlobalLayoutListener

    春节后,又重新“开张”.各位高手请继续支持.谢谢! 原文标题:Kotlin recipes for Android (I): OnGlobalLayoutListener 原文链接:http://an ...

  8. nexus 社区版3.0.2部署、访问

    下载nexus社区办(oss): https://www.sonatype.com/download-oss-sonatype 目前最新版本  nexus-3.0.2-02-win64.zip nex ...

  9. postgresql无法安装pldbgapi的问题

    要对函数进行调试需要安装插件pldbgapi,当初在windows上面的postgresql实例中执行了一下语句就安装上了: create extension pldbgapi; 但是在linux中执 ...

  10. zookeeper(单机/集群)安装与配置

    一.安装与单机配置 1.下载: wget http://archive.apache.org/dist/zookeeper/stable/zookeeper-3.4.6.tar.gz 如果网站下载不了 ...