session - connect-redis session 销毁时卡住?

标签 session authentication node.js connect

我正在 node.js 中实现一个身份验证系统,该系统由用户的 redis 数据库和用于持久、可扩展 session 存储的 connect-redis 支持。

这是我的应用程序的核心,服务器:

// Module Dependencies

var express = require('express');
var redis = require('redis');
var client = redis.createClient();
var RedisStore = require('connect-redis')(express);
var crypto = require('crypto');

var app = module.exports = express.createServer();  

// Configuration

app.configure(function(){
  app.set('views', __dirname + '/views');
  app.set('view engine', 'jade');
  app.use(express.bodyParser());
  app.use(express.methodOverride());
  app.use(express.cookieParser());
  app.use(express.session({ secret: 'obqc487yusyfcbjgahkwfet73asdlkfyuga9r3a4', store: new RedisStore }));
  app.use(require('stylus').middleware({ src: __dirname + '/public' }));
  app.use(app.router);
  app.use(express.static(__dirname + '/public'));
});

app.configure('development', function(){
  app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));
});

app.configure('production', function(){
  app.use(express.errorHandler()); 
});

// Message Helper

app.dynamicHelpers({
  // Index Alerts
  indexMessage: function(req){
    var msg = req.sessionStore.indexMessage;
    if (msg) return '<p class="message">' + msg + '</p>';
  },
  // Login Alerts
  loginMessage: function(req){
    var err = req.sessionStore.loginError;
    var msg = req.sessionStore.loginSuccess;
    delete req.sessionStore.loginError;
    delete req.sessionStore.loginSuccess;
    if (err) return '<p class="error">' + err + '</p>';
    if (msg) return '<p class="success">' + msg + '</p>';
  },
  // Register Alerts
  registerMessage: function(req){
    var err = req.sessionStore.registerError;
    var msg = req.sessionStore.registerSuccess;
    delete req.sessionStore.registerError;
    delete req.sessionStore.registerSuccess;
    if (err) return '<p class="error">' + err + '</p>';
    if (msg) return '<p class="success">' + msg + '</p>';
  },
  // Session Access
  sessionStore: function(req, res){
    return req.sessionStore;
  }
});

// Salt Generator

function generateSalt(){
  var text = "";
  var possible= "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*"
  for(var i = 0; i < 40; i++)
    text += possible.charAt(Math.floor(Math.random() * possible.length));
  return text;
}

// Generate Hash

function hash(msg, key){
  return crypto.createHmac('sha256', key).update(msg).digest('hex');
}

// Authenticate

function authenticate(username, pass, fn){
  client.get('username:' + username + ':uid', function(err, uid){
    if (uid !== null){
      client.hgetall('uid:' + uid, function(err, user){
        if (user.pass == hash(pass, user.salt)){
          return fn(null, user);
        }
        else{
          fn(new Error('invalid password'));
        }
      });
    }
    else{
      return fn(new Error('cannot find user'));
    }
  });
}

function restrict(req, res, next){
  if (req.sessionStore.user) {
    next();
  } else {
    req.sessionStore.loginError = 'Access denied!';
    res.redirect('/login');
  }
}

function accessLogger(req, res, next) {
  console.log('/restricted accessed by %s', req.sessionStore.user.username);
  next();
}

// Routes

app.get('/', function(req, res){
  res.render('index', {
    title: 'TileTabs'
  });
});

app.get('/restricted', restrict, accessLogger, function(req, res){
  res.render('restricted', {
    title: 'Restricted Section'
  });
});

app.get('/logout', function(req, res){
  req.sessionStore.destroy(function(err){
    if (err){
      console.log('Error destroying session...');
    }
    else{
      console.log(req.sessionStore.user.username + ' has logged out.');
      res.redirect('home');
    }
  });
});

app.get('/login', function(req, res){
  res.render('login', {
    title: 'TileTabs Login'
  });
});

app.post('/login', function(req, res){
  var usernameLength = req.body.username.length;
  var passwordLength = req.body.password.length;
  if (usernameLength == 0 && passwordLength == 0){
    req.sessionStore.loginError = 'Authentication failed, please enter a username and password!';
    res.redirect('back');
  }
  else{
    authenticate(req.body.username, req.body.password, function(err, user){
      if (user) {
        req.session.regenerate(function(){
          req.sessionStore.user = user;
          req.sessionStore.indexMessage = 'Authenticated as ' + req.sessionStore.user.name + '.  Click to <a href="/logout">logout</a>. ' + ' You may now access <a href="/restricted">the restricted section</a>.';
          console.log(req.sessionStore.user.username + ' logged in!');
          res.redirect('home');
        });
      } else {
        req.sessionStore.loginError = 'Authentication failed, please check your username and password.';
        res.redirect('back');
      }
    });
  }
});

app.get('/register', function(req, res){
  res.render('register', {
    title: 'TileTabs Register'
  });
});

app.post('/register', function(req, res){
  var name = req.body.name;
  var username = req.body.username;
  var password = req.body.password;
  var salt = generateSalt();

  if (name.length == 0 && username.length == 0 && password.length == 0){
    req.sessionStore.registerError = 'Registration failed, please enter a name, username and password!';
    res.redirect('back');
  }
  else{
    client.get('username:' + username + ':uid', function(err, uid){
      if (uid !== null){
        req.sessionStore.registerError = 'Registration failed, ' + username + ' already taken.';
        res.redirect('back');
      }
      else{
        client.incr('global:nextUserId', function(err, uid){
          client.set('username:' + username + ':uid', uid);
          client.hmset('uid:' + uid, {
            name: name,
            username: username,
            salt: salt,
            pass: hash(password, salt)
          }, function(){
            req.sessionStore.loginSuccess = 'Thanks for registering!  Try logging in!';
            console.log(username + ' has registered!');
            res.redirect('/login');
          });
        });
      }
    });
  }
});

// Only listen on $ node app.js

if (!module.parent) {
  app.listen(80);
  console.log("Express server listening on port %d", app.address().port);
}

注册和登录身份验证工作得很好,但由于某种原因,当连接的用户尝试注销时,我遇到了问题。

正如您从我的 /logout route 看到的,

app.get('/logout', function(req, res){
  req.sessionStore.destroy(function(err){
    if (err){
      console.log('Error destroying session...');
    }
    else{
      console.log(req.sessionStore.user.username + ' has logged out.');
      res.redirect('home');
    }
  });
});

我有两个 console.log 来尝试确定卡住发生的位置。有趣的是,没有记录任何内容。

因此,无论出于何种原因,destroy() 都没有被正确调用。

我不确定我是否只是在语法上犯了错误,或者什么,但根据 connect documentation 看来我的设置是正确的。

最佳答案

您必须使用 req.session 而不是 req.sessionStore。 req.sessionStore 是单个 RedisStore 实例,不会通过 connect 动态设置。这意味着您的代码仅适用于一个用户。您的用户将通过这种方式共享相同的 session 数据。

req.sessionStore 也有一个 destroy 方法,这就是为什么你没有收到任何错误。您的回调未被调用,因为在此方法中回调是第二个参数。

只需在所有代码中将 req.sessionStore 替换为 req.session 即可。例如:

 req.session.destroy(function(err) { ... });

关于session - connect-redis session 销毁时卡住?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6921231/

相关文章:

javascript - ExpressJS ajax发送空数据

java - 如果 session ID 更改,我可以触发某个方法吗?

php - 表单且无 Cookie——提交表单的优雅方式

asp.net-mvc - 使用端口号的 ASP.NET Core 身份验证

javascript - 使用 PHP 和 JavaScript 的提交按钮在登录表单上不起作用

node.js - 如何从 Google Analytics API 获取超过 7 个维度和 10 个指标

node.js - 使用 Node mongodb native 客户端更新的对象计数

java - 如何从 javascript 访问 java.util.List session ?

php - 如果用户尝试从其他位置使用相同的用户名登录,则注销脚本从一个系统/位置启动用户

facebook - 在 Swift 中链接/合并 Facebook 用户和 PFUser(解析)