每个用户一个会话-护照JS

时间:2018-07-19 09:29:31

标签: node.js express passport.js

到目前为止,仅Passport JS入门就可以进行用户登录,根据用户角色来限制页面等。我注意到的一件事是,我可以使用相同的凭据通过多个设备登录。我担心这是因为可以共享同一登录名,这不是我想要的。

我的问题是我该如何停止?我当前的设置只是在单个实例上运行的Express应用程序(尚未实现跨多个实例的负载均衡,到目前为止,我知道我可以使用Redis存储会话详细信息并从那里进行操作) 。但是现在我想知道是否可以通过当前设置使用Passport做到这一点

我已经省略了我认为不必要的部分,以免膨胀问题

# server.js

const session = require('express-session');
const passport = require('passport');

require('./config/passport')(passport); // pass passport for configuration

// Init App
const app = express();


// BodyParser Middleware
app.use(cookieParser()); // read cookies (needed for auth)
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json()); // get information from html forms
app.use(session({
  secret: 'cookie_secret',
  name: 'cookie_name',
  resave: true,
  saveUninitialized: true,
  cookie: { maxAge: 5400000 },
}));
app.use(passport.initialize());
app.use(passport.session());

// routes
require('./routes/routes.js')(app, passport); // load our routes and pass in our app and fully configured passport

const port = process.env.PORT || 4000;
  app.listen(port, () => {
  console.log('listening on port ' + port);
});

护照配置

# config/passport.js
const bcrypt = require('bcryptjs'); // hash password
const LocalStrategy = require('passport-local').Strategy;
const pool = require('./database.js'); // database Connection
const User = require('../models/user'); // load up the user model

module.exports = (passport) => {

  passport.serializeUser((user, done) => {
    done(null, user.id);
  });

  passport.deserializeUser((id, done) => {
    User.findById(id, (err, user) => {
      done(err, user);
    });
  });


  passport.use('local-login', new LocalStrategy({
    usernameField: 'email',
    passwordField: 'password',
    passReqToCallback: true, 
  },
  (req, email, password, done) => {
      pool.query('SELECT * from users where email=$1', [email], (err, result) => {
      if (err) return done(null, false, { message: 'Incorrect Email or Password' });
      if (result.rows.length > 0) {
        const user = result.rows[0];
        bcrypt.compare(password, user.password, (err, isMatch) => {
          if (err) throw err;
          if (isMatch === true) { return done(null, user);
        }
        return done(null, false, { message: 'Incorrect Email or Password' 
      });
     }); // bcrypt
     } else {
      return done(null, false, { message: 'Incorrect Email or Password' });
   } 
  }); 
 }));

};

当用户登录时,他们会执行本地登录策略,然后我便有了方法来检查用户是否可以访问尝试访问的页面

const checkAuthentication = require('../helpers/auth');

// Members Page
app.get('/members', checkAuthentication, async (req, res) => {
  res.render('members');
});

app.post('/login', passport.authenticate('local-login', {
  successRedirect: '/members',
  failureRedirect: '/',
  failureFlash: true,
}));

# auth.js
module.exports = (req, res, next) => {
  if (req.isAuthenticated()) {
    if (new Date(req.user.membership_expiry_date) < new Date()) { // if expiry date < todays date
      req.flash('error', 'Membership Expired');
      return res.redirect('/');
    }
  } else {
    return res.redirect('/');
  }
    return next();
};

我希望在那里有足够的信息可以回顾一下,我希望只允许用户一次登录一个会话,因此一次只能登录一个设备,不能多次登录

谢谢

1 个答案:

答案 0 :(得分:1)

如果要为用户支持单个会话,则需要确保:

任何会话都将永远存在,直到注销为止

通常,会话不会永远存在,但是如果您绝对需要一个登录会话,那么无论会话何时过期,您的用户将无法创建新会话。

您将用户登录数据存储在数据库中

您将在数据库中需要一个is_logged_in或类似字段,只要用户登录或注销,该字段都将更改。如果用户尝试登录,请尝试其登录状态,如果该用户已经登录,则拒绝登录。

您确保妥善处理极端情况

可以很好地处理用户购买新设备但未从以前的设备注销或出现故障的情况。例如,您可以在使用会话24小时后允许登录,但是在这种情况下,如果用户尝试对旧会话执行操作,则您需要自动从旧会话中注销用户。

实际上,您可以通过多种方式实现您想要实现的目标,但是您需要注意细节。