我们到底能走多远系列(36)

扯淡:

  年关将至,总是会在一些时间节点上才感觉时光飞逝,在平时浑浑噩噩的岁月里都浪费掉了太多的宝贵。请珍惜!

  

主题:

     我们在编写http请求处理和响应的代码的时候,经常会处理到session,这里的session是指服务器和客户端交互时把一些信息存在服务器上,下一次请求是,可以在服务器上继续使用这些信息,我们都知道http是无状态的,在服务端维持一个session就是为了解决一些多个请求需要状态维持的问题。

     它的工作原理,我的理解是,在第一次http请求时,服务端在自己内存里创建出一个对应这个客户端的session,往这个session中放好信息后,把标识这个session的唯一字段在响应的时候带给客户端,客户端将这个字段放入cookie,下一次请求的时候客户端就会把这个cookie信息带上来,服务端就可以找出这个客户端对应的session了,也就可以重新使用原来保存的信息。
     这个工作是web容器完成的,像tomcat,jetty, weblogic 都实现了对session相关的接口。
     比如说遇到这样的问题:多个容器分布部署的时候,web容器中的session无法共享,这样可以不把session的信息部存在内存中,而是存在类似redis,memcache这样的数据库中。这样就需要重写处理session的逻辑。修改web容器的源码,或者自己实现以下存取session,和传输解析cookie的方法来模拟session。
java编码中的使用:
 HttpSession session = request. getSession();
session.setAttribute ("uid", 1) ;
session.getAttribute ("uid") ;
  
从原理上来看,实现的流程很清晰,在用node实现web应用的时候,现在流行用express.js。不使用框架的session接口的话,我们自己也可以粗糙的实现一下:
 
// 获得客户端的Cookie
var Cookies = {};
req.headers.cookie && req.headers.cookie.split(';').forEach(function( Cookie ) {
var parts = Cookie.split('=');
Cookies[ parts[ 0 ].trim() ] = ( parts[ 1 ] || '' ).trim();
});
console.info(Cookies); // 设置cookie
res.setHeader(
'Set-Cookie', 'myCookie=test'
);
  
  利用上面的获取cookie和设置cookie,还不能完全完成session维护的流程。还需要服务端存取session值。
     实际上我们在开发普通的web项目的时候,可以了解到,对每一个客户端,服务端都会维护一个session,这些session中存着一些键值对,就像前面jsva代码中看到的,"uid"对应1,然后来查的时候就利用cookies中带上来的id,找到session,再从这个session中查有没有'uid'对应的1。
  

  那么,下面就简单用js来实现一个登录进入一个下载页面,来模拟http session的机制。
 
流程类似如下:
 
首先,先来看一下SessionsManage这个核心模块
var Session = require('./Session');

var SessionsManage = function(expires, clean_time ){
this.def_expires = expires||100; // 过期时间
this.def_clean_time = clean_time||1000; // 执行清除操作间隔时间
this.sessions = {}; // session 放置处
//启动定时任务,就是说不停的会检查去除sessions中过期的的session
setInterval(this.cleanup, this.def_clean_time, this.sessions);
}; var init = function(expires, clean_time){
return new SessionsManage(expires, clean_time);
};
module.exports = SessionsManage;
/**
* 模拟取session
* @param req
* @param name
* @returns {null, session}
*/
SessionsManage.prototype.getSession = function(req, name){
var id = getIdFromCookies(req);
if(id){
// 现货器session对象
var session = this.sessions[id];
if(session && session[name]){
// 再从session对象中找对应的值
return session[name];
}else {
return null;
}
}else{
return null;
}
}; /**
* 模拟存session
* @param req
* @param res
* @param opts
* @returns {boolean}
*/
SessionsManage.prototype.setSession = function(req, res, opts){
if(!opts){
return false;
}else{
// 从cookie中获取id
var id = getIdFromCookies(req) || randomString(36);
var name = opts.name;
var value = opts.value;
var expires = opts.expires || this.def_expires;
if(id && value && name){
// 新创一个session
var session = new Session();
session[name] = value;
session["id"] = id;
session["overLifeTime"] = (+new Date) + expires*1000;
// 放置进sessions
this.sessions[id] = session;
// 写入返回客户端的cookie中
this.setCookieId(res, id, expires);
}
}
}; SessionsManage.prototype.setCookieId = function(res, id, expires){
// config cookie
var d = new Date();
d.setTime(d.getTime() + expires*1000); // in milliseconds
res.setHeader(
'Set-Cookie', 'D_SID='+ id +';expires='+d.toGMTString()+';'
);
}; SessionsManage.prototype.cleanup = function(sessions){
var now = new Date().getTime();
for(var id in sessions){
var session = sessions[id];
if(session.overLifeTime < now){
delete sessions[session.id];
}
}
}; var getIdFromCookies = function(req){
// client's Cookie
var Cookies = {};
req.headers.cookie && req.headers.cookie.split(';').forEach(function( Cookie ) {
var parts = Cookie.split('=');
Cookies[ parts[ 0 ].trim() ] = ( parts[ 1 ] || '' ).trim();
});
console.info(Cookies);
console.info(Cookies["D_SID"]);
if(Cookies["D_SID"]){
return Cookies["D_SID"];
}else{
return null;
}
}; function randomString(bits){
return new Date().getTime();
}
注:这里使用了时间戳作为相互传递的ID,也可以自己产生随机的ID来代替。
 
Session对象:
var Session = function(opt){
if(opt){
this.id = opt.id;
this.overLifeTime = opt.expires;
}
};
module.exports = Session;
  看了SessionManage的代码,只要在登录的时候调用一下setSession,然后再过滤器上调用下getSession,就可以完成上面的流程了。使用express,虽然它自带了session机制,没有使用,模拟过滤器的代码实现在app.js中的:
 
// 正则匹配全部请求
app.get(/^\/*/,function(req, res, next){
if(req.path == "/upload" || req.path == "/doupload"){
var user = SessionsManage.getSession(req,"user");
if(!user){
res.render('login', {});
return;
}
}
next();
});
登录的时候:
 
exports.dologin = function(db, sessions){
return function(req, res) {
console.log("dologin");
var username = req.body.username;
var password = req.body.password;
console.log("username:" + username + "password:" + password);
var collection = db.get('usercollection');
collection.find({},{'username':username,'password':password},function(e,docs){
if(docs){
console.log("username and password is valid");
var opts = {
name : "user",
value : username,
expires : 500
};
// 调用了setSession
sessions.setSession(req,res,opts);
res.render('upload', {});
}else{
console.log("username and password is invalid");
}
});
};
};

  

  以上就基本走完了流程,只要使用express进行一些请求上的配置,就可以了。主要是sessionManage中的实现,用提供出去的几个方法来模拟了整个机制。
  另外,还有一些不足的地方,和遗留的漏洞。
 
 
 
 
 

让我们继续前行

----------------------------------------------------------------------

努力不一定成功,但不努力肯定不会成功。

node模拟http服务器session机制-我们到底能走多远系列(36)的更多相关文章

  1. node实现http上传文件进度条 -我们到底能走多远系列(37)

    我们到底能走多远系列(37) 扯淡: 又到了一年一度的跳槽季,相信你一定准备好了,每每跳槽,总有好多的路让你选,我们的未来也正是这一个个选择机会组合起来的结果,所以尽可能的找出自己想要的是什么再做决定 ...

  2. ThreadPoolExecutor机制探索-我们到底能走多远系列(41)

    我们到底能走多远系列(41) 扯淡: 这一年过的不匆忙,也颇多感受,成长的路上难免弯路,这个世界上没人关心你有没有变强,只有自己时刻提醒自己,不要忘记最初出发的原因. 其实这个世界上比我们聪明的人无数 ...

  3. js中this和回调方法循环-我们到底能走多远系列(35)

    我们到底能走多远系列(35) 扯淡: 13年最后一个月了,你们在13年初的计划实现了吗?还来得及吗? 请加油~ 主题: 最近一直在写js,遇到了几个问题,可能初入门的时候都会遇到吧,总结下. 例子: ...

  4. Spring mvc源码url路由-我们到底能走多远系列(38)

    我们到底能走多远系列38 扯淡: 马航的事,挺震惊的.还是多多珍惜身边的人吧. 主题: Spring mvc 作为表现层的框架,整个流程是比较好理解的,毕竟我们做web开发的,最早也经常接触的就是一个 ...

  5. 服务调用方案(Spring Http Invoker) - 我们到底能走多远系列(40)

    我们到底能走多远系列(40) 扯淡:  判断是否加可以效力于这家公司,一个很好的判断是,接触下这公司工作几年的员工,了解下生活工作状态,这就是你几年后的状态,如果满意就可以考虑加入了. 主题: 场景: ...

  6. Sharded实现学习-我们到底能走多远系列(32)

    我们到底能走多远系列(32) 扯淡: 工作是容易的赚钱是困难的 恋爱是容易的成家是困难的 相爱是容易的相处是困难的 决定是容易的可是等待是困难的 主题: 1,Sharded的实现    Sharded ...

  7. JMS生产者+单线程发送-我们到底能走多远系列(29)

    我们到底能走多远系列(29) 扯淡: “然后我俩各自一端/望着大河弯弯/终于敢放胆/嘻皮笑脸/面对/人生的难”      --- <山丘> “迎着风/迎向远方的天空/路上也有艰难/也有那解 ...

  8. ArrayBlockingQueue-我们到底能走多远系列(42)

    我们到底能走多远系列(42) 扯淡: 乘着有空,读些juc的源码学习下.后续把juc大致走一边,反正以后肯定要再来. 主题: BlockingQueue 是什么 A java.util.Queue t ...

  9. 初始化IoC容器(Spring源码阅读)-我们到底能走多远系列(31)

    我们到底能走多远系列(31) 扯淡: 有个问题一直想问:各位你们的工资剩下来会怎么处理?已婚的,我知道工资永远都是不够的.未婚的你们,你们是怎么分配工资的? 毕竟,对自己的收入的分配差不多体现了自己的 ...

随机推荐

  1. LoadRunner 文本检查点使用

    在测试时,我们可以根据文本检查点判断事务是否执行正确.   我们在启动流程时,成功返回:   我们检查这个响应结果是否有. int i=0; i=web_reg_find("Text=\&q ...

  2. linux percona-toolkit的安装

    percona-toolkit是一套linux下的工具集,我们需要使用到 pt-query-digest 对mysql慢日志查询来做分析. 1.下载 http://www.percona.com/do ...

  3. JS解析json数据并将json字符串转化为数组的实现方法

    json数据在ajax实现异步交互时起到了很重要的作用,他可以返回请求的数据,然后利用客户端的js进行解析,这一点体现出js的强大,本文介绍JS解析json数据并将json字符串转化为数组的实现方法, ...

  4. 使用Apache HttpClient

    在一般情况下,如果只是需要Web站点的某个简单页面提交请求并获取服务器响应,完全可以使用前面所介绍的HttpConnection来完成.但在绝大部分情况下,Web站点的网页可能没这么简单,这些页面并不 ...

  5. NSString asscii格式(2进制) 转 utf8格式——解决iOS自己处理http socket数据,遇到Transfer-Encoding: chunked时

    因为需要实现自己的http客户端,就要自己模拟http 的socket通讯: 上行不难,自己处理好http即可. 但下行时,服务器端的动态语言返回数据有可能会是这种格式: http头 16进制表示的数 ...

  6. C# 正则表达式 验证:数字、带小数点数字、电话和手机

    一.带小数点数字 public static bool IsNumber(string input) { string pattern = "^-?\\d+$|^(-?\\d+)(\\.\\ ...

  7. java枚举类

    enum关键字用于定义枚举类,若枚举只有一个成员, 则可以作为一种单例模式的实现方式.   枚举类对象的属性不应允许被改动, 所以应该使用 private final 修饰. 枚举类的使用 priva ...

  8. 经典线程同步 互斥量Mutex

    阅读本篇之前推荐阅读以下姊妹篇: <秒杀多线程第四篇一个经典的多线程同步问题> <秒杀多线程第五篇经典线程同步关键段CS> <秒杀多线程第六篇经典线程同步事件Event& ...

  9. 用VNC远程图形化连接Linux桌面的配置方法

    首先下载到vnc的linux版本和windows版本. 1. 首先,我们要先下载 VNC 安装,假如我们下载的目录是 VNC/vncgo . # cd ~# wget http://www.realv ...

  10. Cocoa Drawing

    Graphics Contexts Graphics contexts are a fundamental part of the drawing infrastructure in Cocoa ap ...