zoukankan      html  css  js  c++  java
  • node模拟http服务器session机制-我们到底能走多远系列(36)

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

    让我们继续前行

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

    努力不一定成功,但不努力肯定不会成功。
  • 相关阅读:
    Linux下部署svn服务
    eclipse3.3.2在CentOS5.4下启动时崩溃的解决方法
    Cassandra配置
    Hadoop系列相关优秀网站收集
    ul li 制作导航栏 程序员
    一.Java访问权限饰词(access specifiers) 程序员
    Java程序员应该了解的10个面向对象设计原则 程序员
    MyEclipse里更改字体大小和快捷建的设置 程序员
    JFileChooser 为用户选择文件提供了一种简单的机制 程序员
    JavaScript时间函数总结 程序员
  • 原文地址:https://www.cnblogs.com/killbug/p/3488051.html
Copyright © 2011-2022 走看看