express下socket.io使用session验证用户

express下使用socket.io来传输用户间的即时消息,如何验证却是个问题,想了一下可以在传输数据中添加一个用于验证的加密字段,不过好像这样有点麻烦,另外一种在io.set('authorization',function{..})中通过handshakeData.headers.cookie获取express下的session来验证,不过socket.io 1.x已经不再使用这种方式,结果绕了很多弯路才找到正确方式,现在使用socket.request.headers.cookie,下面是用法,以此记下

导航目录

  1. 导入模块
  2. 配置express中间件
  3. socket.io获取session验证
  4. session和cookie的理解

  1. 导入相应模块
    使用express框架模块,并引入cookie解析和session存储模块:

     var express = require('express'),// 导入express框架模块
     //cookieParser = require('cookie-parser'),//没有使用express可以使用分离的cookie-parser模块进行解析
     parseCookie = express.cookieParser("secret"),//"secret"改为你的加密签名
     //建立一个memory store的实例
     storeMemory = new express.session.MemoryStore();
     
    

  1. express使用session配置:

     //中间件
     app.use(express.bodyParser());
     app.use(express.cookieParser());    //解析cookie
     //使用secret设置签名来加密,使用key自定义cookie键,store设置存储方式为内存存储方式
     app.use(express.session({secret:'secret',key:'connect_sid',store:storeMemory}));
     
    

  1. socket.io获取session验证

    socket.io 0.9.x版本中使用io.set('authorization',function{..}):

     var io = sio.listen(app);
     //设置session
     io.set('authorization', function(handshakeData, callback){
         // 解析客户端的cookie字符串
         handshakeData.cookie = parseCookie(handshakeData.headers.cookie)
         var connect_sid = handshakeData.cookie['connect_sid']; //connect_sid即我们之前自定义的cookie键字段
         if (connect_sid) {
             //通过cookie中保存的session的id获取到服务器端对应的session
             storeMemory.get(connect_sid, function(error, session){
                 if (error) {
                     // if we cannot grab a session, turn down the connection
                     callback(error.message, false);
                 }
                 else {
                     // save the session data and accept the connection
                     handshakeData.session = session;
                     callback(null, true);
                 }
             });
         }
         else {
             callback('nosession');
         }
     });
    

    使用io.use(function (socket,next){..})代替io.set('authorization',function{..}),socket.request.headers.cookie代替handshakeData.headers.cookie:

     io = sio.listen(app);
     //设置socket的session验证
     io.use(function (socket,next) {
         //parseCookie会解析socket.request.headers.cookie并赋值给执行socket.request.cookies
         parseCookie(socket.request, null, function(err) {
             if (err) {
                 console.log("err parse");
                 return next(new Error("cookie err"));
             }
             // cookie中获取sessionId
             var connect_sid = socket.request.cookies['connect_sid'];
             if (connect_sid) {
                 //通过cookie中保存的session的id获取到服务器端对应的session
                 storeMemory.get(connect_sid, function(error, session){
                     if (error) {
                         return next(new Error('Authentication error'));
                     }
                     else {
                         // save the session data and accept the connection
                         socket.request.session = session;
                         next();
                     }
                 });
             }
         });
    
     });
    

    现在可以在socket.request.session中看到我们的session数据了:

     io.sockets.on('connection', function (socket){
         var session = socket.handshake.session;//session
         var name = session.name;
         var password = session.password;
         console.log(name+' '+password);
     });
    

    11.29更新

    express 4.x可以直接把express-session中间件当成socket.io处理,所以可以简单像下面这样,就可以共享express的session了

     sio.use(function(socket, next) {
         sessionMiddleware(socket.request, socket.request.res, next);
     });
    

    example:

     var Server = require("http").Server;
     var session = require("express-session");
    
     var app = express();
     var server = Server(app);
     var sio = require("socket.io")(server);
    
     var sessionMiddleware = session({
         secret: "keyboard cat",
         resave: false,
         saveUninitialized: true
     });
    
     sio.use(function(socket, next) {
         sessionMiddleware(socket.request, socket.request.res, next);
     });
    
     app.use(sessionMiddleware);
    
     app.get("/", function(req, res){
         req.session.username = 'jim';
     });
    
     sio.sockets.on("connection", function(socket) {
       console.log(socket.request.session.username); // 'jim'
     });
    
     server.listen(80);
     
    

  1. 区别session和cookie

    看了这么多,也对session和cookie的概念有些深入了解了。session保存在服务器,而cookie保存在浏览器上,当你浏览网页时,服务器会请求浏览器通过设置cookie来保存一些数据,cookie的内容主要包括:名字,值,过期时间,路径和域,其中如果过期时间不设置就表示浏览器关闭即删除该cookie。

    而服务器程序创建session时,会检查浏览器cookie中是否保存有session id的字段,此字段映射到服务器一个对应的session存储区,服务器通过session id找到对应的session来保存数据,而当浏览器中没有session id的时候,服务器端会创建一个新的session并生成一个session id保存在浏览器cookie中。

    所以session解决了cookie的一些不安全问题,但是同时session保存在服务器上会占用一些服务器性能,因此一些私密数据应当使用session保存,而其他信息保存为cookie。

    另外还有一个之前有些困惑的地方现在也明白些了:express中使用cookieParser对session id进行签名加密,而parseCookie对应就是对cookie进行解签。附上一段cookie-parser模块中处理cookie的代码:

     exports.signedCookies = function(obj, secret){
       var cookies = Object.keys(obj); 
       var dec; 
       var key;
       var ret = Object.create(null);
       var val;
    
       for (var i = 0; i < cookies.length; i++) {
         key = cookies[i]; 
         val = obj[key];   
         dec = exports.signedCookie(val, secret); 
    
         if (val !== dec) { 
           ret[key] = dec;
           delete obj[key];
         }
       }
    
       return ret;
     };
    
     exports.signedCookie = function(str, secret){ 
       return str.substr(0, 2) === 's:'
         ? signature.unsign(str.slice(2), secret)
         : str;
     };
    

Demo下载:Session-Demo.zip

标签: nodejs, session, cookie, socket.io, express

已有 3 条评论

  1. haolee haolee

    // save the session data and accept the connection
    socket.request.session = session;
    next();

    -----
    上面的这一句保存session到socket中,以便后面的connection回调中直接取,但我按你的方法试了一下,没有取到session信息,我的socketio版本号是"socket.io": "^1.6.0"

    1. littlejim littlejim

      sessionid确定拿到了吗?有的话肯定可以通过sessionid从store里拿到session的哦,还有在之前有数据赋给session么,没有的话session也是空的。

      另外可以直接把express的express-session当作socketio的session中间件,传了个demo,你可以参考下。

    2. ddd ddd

      undefined

添加新评论