1、什么是session?html
最近在学习node.js 的express框架,接触到了关于session方面的内容。翻阅了一些的博客,学到了很多东西,发现一篇博文讲的很好,概念内容摘抄以下:node
Session是什么 Session通常译做会话,牛津词典对其的解释是进行某活动连续的一段时间。从不一样的层面看待session,它有着相似但不全然相同的含义。好比,在web应用的用户看来,他打开浏览器访问一个电子商务网站,登陆、并完成购物直到关闭浏览器,这是一个会话。而在web应用的开发者开来,用户登陆时我须要建立一个数据结构以存储用户的登陆信息,这个结构也叫作session。所以在谈论session的时候要注意上下文环境。而本文谈论的是一种基于HTTP协议的用以加强web应用能力的机制或者说一种方案,它不是单指某种特定的动态页面技术,而这种能力就是保持状态,也能够称做保持会话。 为何须要session 谈及session通常是在web应用的背景之下,咱们知道web应用是基于HTTP协议的,而HTTP协议偏偏是一种无状态协议。也就是说,用户从A页面跳转到B页面会从新发送一次HTTP请求,而服务端在返回响应的时候是没法获知该用户在请求B页面以前作了什么的。 对于HTTP的无状态性的缘由,相关RFC里并无解释,但联系到HTTP的历史以及应用场景,咱们能够推测出一些理由: 1. 设计HTTP最初的目的是为了提供一种发布和接收HTML页面的方法。那个时候没有动态页面技术,只有纯粹的静态HTML页面,所以根本不须要协议能保持状态; 2. 用户在收到响应时,每每要花一些时间来阅读页面,所以若是保持客户端和服务端之间的链接,那么这个链接在大多数的时间里都将是空闲的,这是一种资源的无故浪费。因此HTTP原始的设计是默认短链接,即客户端和服务端完成一次请求和响应以后就断开TCP链接,服务器所以没法预知客户端的下一个动做,它甚至都不知道这个用户会不会再次访问,所以让HTTP协议来维护用户的访问状态也全然没有必要; 3. 将一部分复杂性转嫁到以HTTP协议为基础的技术之上可使得HTTP在协议这个层面上显得相对简单,而这种简单也赋予了HTTP更强的扩展能力。事实上,session技术从本质上来说也是对HTTP协议的一种扩展。 总而言之,HTTP的无状态是由其历史使命而决定的。但随着网络技术的蓬勃发展,人们不再知足于死板乏味的静态HTML,他们但愿web应用能动起来,因而客户端出现了脚本和DOM技术,HTML里增长了表单,而服务端出现了CGI等等动态技术。 而正是这种web动态化的需求,给HTTP协议提出了一个难题:一个无状态的协议怎样才能关联两次连续的请求呢?也就是说无状态的协议怎样才能知足有状态的需求呢? 此时有状态是必然趋势而协议的无状态性也是木已成舟,所以咱们须要一些方案来解决这个矛盾,来保持HTTP链接状态,因而出现了cookie和session。 对于此部份内容,读者或许会有一些疑问,笔者在此先谈两点: 1. 无状态性和长链接 可能有人会问,如今被普遍使用的HTTP1.1默认使用长链接,它仍是无状态的吗? 链接方式和有无状态是彻底没有关系的两回事。由于状态从某种意义上来说就是数据,而链接方式只是决定了数据的传输方式,而不能决定数据。长链接是随着计算机性能的提升和网络环境的改善所采起的一种合理的性能上的优化,通常状况下,web服务器会对长链接的数量进行限制,以避免资源的过分消耗。 2. 无状态性和session Session是有状态的,而HTTP协议是无状态的,两者是否矛盾呢? Session和HTTP协议属于不一样层面的事物,后者属于ISO七层模型的最高层应用层,前者不属于后者,前者是具体的动态页面技术来实现的,但同时它又是基于后者的。在下文中笔者会分析Servlet/Jsp技术中的session机制,这会使你对此有更深入的理解。 Cookie和Session 上面提到解决HTTP协议自身无状态的方式有cookie和session。两者都能记录状态,前者是将状态数据保存在客户端,后者则保存在服务端。 首先看一下cookie的工做原理,这须要有基本的HTTP协议基础。 cookie是在RFC2109(已废弃,被RFC2965取代)里初次被描述的,每一个客户端最多保持三百个cookie,每一个域名下最多20个Cookie(实际上通常浏览器如今都比这个多,如Firefox是50个),而每一个cookie的大小为最多4K,不过不一样的浏览器都有各自的实现。对于cookie的使用,最重要的就是要控制cookie的大小,不要放入无用的信息,也不要放入过多信息。 不管使用何种服务端技术,只要发送回的HTTP响应中包含以下形式的头,则视为服务器要求设置一个cookie: Set-cookie:name=name;expires=date;path=path;domain=domain 支持cookie的浏览器都会对此做出反应,即建立cookie文件并保存(也多是内存cookie),用户之后在每次发出请求时,浏览器都要判断当前全部的cookie中有没有没失效(根据expires属性判断)而且匹配了path属性的cookie信息,若是有的话,会如下面的形式加入到请求头中发回服务端: Cookie: name="zj"; Path="/linkage" 服务端的动态脚本会对其进行分析,并作出相应的处理,固然也能够选择直接忽略。 这里牵扯到一个规范(或协议)与实现的问题,简单来说就是规范规定了作成什么样子,那么实现就必须依据规范来作,这样才能互相兼容,可是各个实现所使用的方式却不受约束,也能够在实现了规范的基础上超出规范,这就称之为扩展了。不管哪一种浏览器,只要想提供cookie的功能,那就必须依照相应的RFC规范来实现。因此这里服务器只管发Set-cookie头域,这也是HTTP协议无状态性的一种体现。 须要注意的是,出于安全性的考虑,cookie能够被浏览器禁用。 再看一下session的原理: 笔者没有找到相关的RFC,由于session本就不是协议层面的事物。它的基本原理是服务端为每个session维护一份会话信息数据,而客户端和服务端依靠一个全局惟一的标识来访问会话信息数据。用户访问web应用时,服务端程序决定什么时候建立session,建立session能够归纳为三个步骤: 1. 生成全局惟一标识符(sessionid); 2. 开辟数据存储空间。通常会在内存中建立相应的数据结构,但这种状况下,系统一旦掉电,全部的会话数据就会丢失,若是是电子商务网站,这种事故会形成严重的后果。不过也能够写到文件里甚至存储在数据库中,这样虽然会增长I/O开销,但session能够实现某种程度的持久化,并且更有利于session的共享; 3. 将session的全局惟一标示符发送给客户端。 问题的关键就在服务端如何发送这个session的惟一标识上。联系到HTTP协议,数据无非能够放到请求行、头域或Body里,基于此,通常来讲会有两种经常使用的方式:cookie和URL重写。 1. Cookie 读者应该想到了,对,服务端只要设置Set-cookie头就能够将session的标识符传送到客户端,而客户端此后的每一次请求都会带上这个标识符,因为cookie能够设置失效时间,因此通常包含session信息的cookie会设置失效时间为0,即浏览器进程有效时间。至于浏览器怎么处理这个0,每一个浏览器都有本身的方案,但差异都不会太大(通常体如今新建浏览器窗口的时候); 2. URL重写 所谓URL重写,顾名思义就是重写URL。试想,在返回用户请求的页面以前,将页面内全部的URL后面所有以get参数的方式加上session标识符(或者加在path info部分等等),这样用户在收到响应以后,不管点击哪一个连接或提交表单,都会在再带上session的标识符,从而就实现了会话的保持。读者可能会以为这种作法比较麻烦,确实是这样,可是,若是客户端禁用了cookie的话,URL重写将会是首选。 到这里,读者应该明白我前面为何说session也算做是对HTTP的一种扩展了吧。以下两幅图是笔者在Firefox的Firebug插件中的截图,能够看到,当我第一次访问index.jsp时,响应头里包含了Set-cookie头,而请求头中没有。当我再次刷新页面时,图二显示在响应中不在有Set-cookie头,而在请求头中却有了Cookie头。注意一下Cookie的名字:jsessionid,顾名思义,就是session的标识符,另外能够看到两幅图中的jsessionid的值是相同的,缘由笔者就再也不多解释了。另外读者可能在一些网站上见过在最后附加了一段形如jsessionid=xxx的URL,这就是采用URL重写来实现的session。
(图一,首次请求index.jsp)
(图二,再次请求index.jsp) Cookie和session因为实现手段不一样,所以也各有优缺点和各自的应用场景: 1. 应用场景 Cookie的典型应用场景是Remember Me服务,即用户的帐户信息经过cookie的形式保存在客户端,当用户再次请求匹配的URL的时候,帐户信息会被传送到服务端,交由相应的程序完成自动登陆等功能。固然也能够保存一些客户端信息,好比页面布局以及搜索历史等等。 Session的典型应用场景是用户登陆某网站以后,将其登陆信息放入session,在之后的每次请求中查询相应的登陆信息以确保该用户合法。固然仍是有购物车等等经典场景; 2. 安全性 cookie将信息保存在客户端,若是不进行加密的话,无疑会暴露一些隐私信息,安全性不好,通常状况下敏感信息是通过加密后存储在cookie中,但很容易就会被窃取。而session只会将信息存储在服务端,若是存储在文件或数据库中,也有被窃取的可能,只是可能性比cookie小了太多。 Session安全性方面比较突出的是存在会话劫持的问题,这是一种安全威胁,这在下文会进行更详细的说明。整体来说,session的安全性要高于cookie; 3. 性能 Cookie存储在客户端,消耗的是客户端的I/O和内存,而session存储在服务端,消耗的是服务端的资源。可是session对服务器形成的压力比较集中,而cookie很好地分散了资源消耗,就这点来讲,cookie是要优于session的; 4. 时效性 Cookie能够经过设置有效期使其较长时间内存在于客户端,而session通常只有比较短的有效期(用户主动销毁session或关闭浏览器后引起超时); 5. 其余 Cookie的处理在开发中没有session方便。并且cookie在客户端是有数量和大小的限制的,而session的大小却只以硬件为限制,能存储的数据无疑大了太多。
后文中我会主要针对express的session专门讲解。主要参考的博客网址以下,并对博主的无私奉献表示万分感谢。git
http://www.cnblogs.com/shoru/archive/2010/02/19/1669395.html (大话session)github
http://blog.csdn.net/fangaoxin/article/details/6952954 (Cookie/Session机制详解)web
2、express框架之session 内存存储mongodb
express-session 是基于express框专门用于处理session的中间件。这里不谈express-session怎么安装,只给出相应的实例代码。另外,session的认证机制离不开cookie,须要同时使用cookieParser 中间件,有关的介绍能够专门参考https://github.com/expressjs/session/blob/master/README.md,或者参考http://blog.modulus.io/nodejs-and-express-sessions,这个博客上讲的比较清楚。数据库
1 var express = require('express'); 2 var session = require('express-session'); 3 var cookieParser = require('cookie-parser'); 4 5 var app = express(); 6 7 app.use(cookieParser()); 8 app.use(session({ 9 secret: '12345', 10 name: 'testapp', //这里的name值得是cookie的name,默认cookie的name是:connect.sid 11 cookie: {maxAge: 80000 }, //设置maxAge是80000ms,即80s后session和相应的cookie失效过时 12 resave: false, 13 saveUninitialized: true, 14 })); 15 16 17 app.get('/awesome', function(req, res){ 18 19 if(req.session.lastPage) { 20 console.log('Last page was: ' + req.session.lastPage + "."); 21 } 22 req.session.lastPage = '/awesome'; //每一次访问时,session对象的lastPage会自动的保存或更新内存中的session中去。 23 res.send("You're Awesome. And the session expired time is: " + req.session.cookie.maxAge); 24 }); 25 26 app.get('/radical', function(req, res){ 27 if (req.session.lastPage) { 28 console.log('Last page was: ' + req.session.lastPage + "."); 29 } 30 req.session.lastPage = '/radical'; 31 res.send('What a radical visit! And the session expired time is: ' + req.session.cookie.maxAge); 32 }); 33 34 app.get('/tubular', function(req, res){ 35 if (req.session.lastPage){ 36 console.log("Last page was: " + req.session.lastPage + "."); 37 } 38 39 req.session.lastPage = '/tubular'; 40 res.send('Are you a suffer? And the session expired time is: ' + req.session.cookie.maxAge); 41 }); 42 43 44 app.listen(5000);
2.1 express-session中间件的使用:express
只须要用express app的use方法将session挂载在‘/’路径便可,这样全部的路由均可以访问到session。能够给要挂载的session传递不一样的option参数,来控制session的不一样特性。具体能够参见官网:https://github.com/expressjs/session/blob/master/README.md。浏览器
2.2 session内容的存储和更改:安全
To store or access session data, simply use the request property req.session, which is (generally) serialized as JSON by the store, so nested objects are typically fine.
一旦咱们将express-session中间件用use挂载后,咱们能够很方便的经过req参数来存储和访问session对象的数据。req.session是一个JSON格式的JavaScript对象,咱们能够在使用的过程当中随意的增长成员,这些成员会自动的被保存到option参数指定的地方,默认即为内存中去。
2.3 session的生命周期
session与发送到客户端浏览器的生命周期是一致的。而咱们在挂载session的时候,经过option选项的cookie.maxAge成员,咱们能够设置session的过时时间,以ms为单位(可是,若是session存储在mongodb中的话,任何低于60s(60000ms)的设置是没有用的,下文会有详细的解释)。若是maxAge不设置,默认为null,这样的expire的时间就是浏览器的关闭时间,即每次关闭浏览器的时候,session都会失效。
3、express框架之session 数据库存储
有时候,咱们须要session的声明周期要长一点,好比好多网站有个免密码两周内自动登陆的功能。基于这个需求,session必须寻找内存以外的存储载体,数据库能提供完美的解决方案。这里,我选用的是mongodb数据库,做为一个NoSQL数据库,它的基础数据对象时database-collection-document 对象模型很是直观并易于理解,针对node.js 也提供了丰富的驱动和API。express框架提供了针对mongodb的中间件:connect-mongo,咱们只需在挂载session的时候在options中传入mongodb的参数便可,程序运行的时候, express app 会自动的替咱们管理session的存储,更新和删除。具体能够参考:
https://github.com/kcbanner/connect-mongo
测试代码以下:
1 var express = require('express'); 2 var session = require('express-session'); 3 var cookieParser = require('cookie-parser'); 4 var MongoStore = require('connect-mongo')(session); 5 var app = express(); 6 7 app.use(cookieParser()); 8 app.use(session({ 9 secret: '12345', 10 name: 'testapp', 11 cookie: {maxAge: 80000 }, 12 resave: false, 13 saveUninitialized: true, 14 store: new MongoStore({ //建立新的mongodb数据库 15 host: 'localhost', //数据库的地址,本机的话就是127.0.0.1,也能够是网络主机 16 port: 27017, //数据库的端口号 17 db: 'test-app' //数据库的名称。 18 }) 19 })); 20 21 22 app.get('/awesome', function(req, res){ 23 24 if(req.session.lastPage) { 25 console.log('Last page was: ' + req.session.lastPage + "."); 26 } 27 req.session.lastPage = '/awesome'; 28 res.send("You're Awesome. And the session expired time is: " + req.session.cookie.maxAge); 29 }); 30 31 app.get('/radical', function(req, res){ 32 if (req.session.lastPage) { 33 console.log('Last page was: ' + req.session.lastPage + "."); 34 } 35 req.session.lastPage = '/radical'; 36 res.send('What a radical visit! And the session expired time is: ' + req.session.cookie.maxAge); 37 }); 38 39 app.get('/tubular', function(req, res){ 40 if (req.session.lastPage){ 41 console.log("Last page was: " + req.session.lastPage + "."); 42 } 43 44 req.session.lastPage = '/tubular'; 45 res.send('Are you a suffer? And the session expired time is: ' + req.session.cookie.maxAge); 46 }); 47 48 49 app.listen(5000);
跟session的内存存储同样,只需增长红色部分的store选项便可,app会自动替咱们把session存入到mongodb数据,而非内存中。
3.1 session的生命周期:
因为session是存在服务器端数据库的,因此的它的生命周期能够持久化,而不只限于浏览器关闭的时间。具体是由cookie.maxAge 决定:若是maxAge设定是1个小时,那么从这个因浏览器访问服务器致使session建立开始后,session会一直保存在服务器端,即便浏览器关闭,session也会继续存在。若是此时服务器宕机,只要开机后数据库没发生不可逆转的破坏,maxAge时间没过时,那么session是能够继续保持的。
当maxAge时间过时后,session会自动的数据库中移除,对应的还有浏览器的cookie。不过,因为connect-mongo的特殊机制(每1分钟检查一次过时session),session的移除可能在时间上会有必定的滞后。
connect-mongo uses MongoDB's TTL collection feature (2.2+) to have mongod automatically remove expired sessions. (mongod runs this check every minute.) Note: By connect/express's default, session cookies are set to expire when the user closes their browser (maxAge: null). In accordance with standard industry practices, connect-mongo will set these sessions to expire two weeks from their last 'set'. You can override this behavior by manually setting the maxAge for your cookies -- just keep in mind that any value less than 60 seconds is pointless, as mongod will only delete expired documents in a TTL collection every minute.
固然,因为cookie是由浏览器厂商实现的,cookie不具备跨浏览器的特性,例如,我用firefox浏览器在京东上购物时,勾选了2周内免密码输入,可是当我第一次用IE登录京东时,一样要从新输入密码。因此,这对服务器的同一个操做,不一样的浏览器发起的请求,会产生不一样的session-cookie。
转自:http://www.cnblogs.com/chenchenluo/p/4197181.html