代码之家  ›  专栏  ›  技术社区  ›  Richlewis

每个用户一个会话-passport JS

  •  0
  • Richlewis  · 技术社区  · 6 年前

    刚开始使用PassportJS,到目前为止可以有一个用户登录,根据用户角色限制页面等等。但我注意到的一件事是,我可以使用相同的凭据通过多个设备登录。这是我关心的,因为同样的登录可以共享,这不是我想要的。

    我的问题是我怎样才能阻止这一切?我目前的设置只是一个运行在单个实例上的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 回复  |  直到 6 年前
        1
  •  1
  •   Lajos Arpad    6 年前

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

    任何会话都将永远有效,或者直到注销

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

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

    您将需要一个is_logged_in或数据库中的类似字段,当用户登录或注销时,该字段将被更改。如果用户试图登录,则尝试其登录状态,如果用户已登录,则拒绝登录。

    你要确保极端情况得到妥善处理

    用户购买新设备而没有从以前的设备注销,或者设备损坏等情况都得到了很好的处理。例如,您可以允许在使用会话24小时后登录,但在这种情况下,如果用户试图对旧会话执行某些操作,则需要自动从旧会话中注销用户。

    事实上,你可以在很多方面达到你想要达到的目标,但是你需要注意细节。