node模拟http服务器session机制-我们到底能走多远系列(36)总结

熊孩纸 阅读:201 2021-05-02 15:05:52 评论:0

我们到底能走多远系列(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;
View Code
  看了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中的实现,用提供出去的几个方法来模拟了整个机制。
  另外,还有一些不足的地方,和遗留的漏洞。
 
 
 
 
 

让我们继续前行

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

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

声明

1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。

发表评论
搜索
排行榜
KIKK导航

KIKK导航

关注我们