Passport中间件,检查用户是否已经有来自

时间:2016-12-20 21:31:39

标签: express passport.js angular-fullstack

我正在使用angular-fullstack构建一个Web应用程序。堆栈使用express-sessions进行会话存储(在Mongodb中),使用passport.js进行身份验证。

我想将每个用户限制为单个登录会话。我正在尝试找到一种方法来检查用户在登录时是否已经有了生活会话。

有没有办法以编程方式调用路由来从护照中间件查询mongodb?

'use strict';

import path from 'path';

import passport from 'passport';
import {Strategy as LocalStrategy} from 'passport-local';

import express from 'express';
import session from 'express-session';

import _ from 'lodash';
import Session from '../../api/session/session.model';

var app = express();
require('run-middleware')(app);

function localAuthenticate(User, email, password, done, req) {
  User.findOne({
    email: email.toLowerCase()
  }).exec()
    .then(user => {

      if (!user) {
        return done(null, false, {
          message: 'This email is not registered.'
        });
      }

      // HERE is where I am trying to check if a user
      // already has a living session when they login

      // I tried to use the runMiddleware
      // to query mongodb for all the existing sessions
      // but I get this error: http://pastebin.com/YTeu5AwA
      app.runMiddleware('/sessions',{},function(code,data){
        console.log(code) // 200 
        console.log(data) // { user: '20', name: 'Moyshale' }
      });

      // Is there a way to access and use an existing route?

      user.authenticate(password, function(authError, authenticated) {
        if (authError) {
          return done(authError);
        }
        if (!authenticated) {
          return done(null, false, { message: 'This password is not correct.' });
        } else {
          return done(null, user);
        }
      });
    })
    .catch(err => done(err));
}

export function setup(User, config) {

  passport.use(new LocalStrategy({
    passReqToCallback: true,
    usernameField: 'email',
    passwordField: 'password' // this is the virtual field on the model
  }, function(req, email, password, done) {
    return localAuthenticate(User, email, password, done, req);
  }));
}

1 个答案:

答案 0 :(得分:0)

好的,我想出来了,我会试着解释一下我做了什么。我的具体实现要求我设置用户“席位”,其中每个用户都是一个组的一部分,每个组一次限制N次登录。

enter image description here

正如我在问题中所提到的,我正在使用角度fullstack yeoman生成器,因此该解决方案特定于该设置。

  1. 我创建了一个'sessions'API endpoint,以便我可以查询和修改存储在mongo db中的会话。我在会话模型中添加了一个类型为Number的“座位”记录。这用于跟踪每个会话的用户座位状态。每个用户都有一个'loginSeat'值,用于填充此字段。此外,会话现在有一个seatAllowed类型为Boolean,true:允许用户访问该站点,false:不允许用户访问该站点。

    'use strict';
    
    import mongoose from 'mongoose';
    
    var SessionSchema = new mongoose.Schema({
      _id: String,
      session: String,
      expires: Date,
      seat: Number,
      seatAllowed: Boolean // true: the user is allowed to access the site, false: the user is not allowed access to the site
    });
    
    export default mongoose.model('Session', SessionSchema); 
    
  2. 我修改了server/auth/login/passport.js,以便当用户登录该网站时,所有其他具有匹配席位的用户都会被淘汰。

    'use strict';
    
    import path from 'path';
    
    import passport from 'passport';
    import {Strategy as LocalStrategy} from 'passport-local';
    import _ from 'lodash';
    import Sessions from '../../api/session/session.model';
    
    function saveUpdates(updates) {
      return function(entity) {
        var updated = _.merge(entity, updates);
        return updated.save()
          .then(updated => {
            return updated;
          });
      };
    }
    
    function localAuthenticate(User, email, password, done, req) {
      User.findOne({
        email: email.toLowerCase()
      }).exec()
        .then(user => {
          if (!user) {
            return done(null, false, {
              message: 'This email is not registered.'
            });
          }
    
          // When a user logs into the site we flag their seat as allowed
          var updateSession = {'seat': user.loginSeat, 'seatAllowed': true};
    
          Sessions.findById(req.session.id).exec()
            .then(saveUpdates(updateSession))
    
          // When a user logs into the site, we disallow the seats of all other sessions with matching seat
          Sessions.find().exec()
            .then(sessions => {
    
            // Check for existing user logged in with matching login seat
            for (var i = 0; i < sessions.length; i++) {
              if (sessions[i].seat === user.loginSeat && sessions[i].id !== req.session.id) {
                console.log('DISALOW SEAT:');
                var updateSession = {'seatAllowed': false};
    
                Sessions.findById(sessions[i].id).exec()
                  .then(saveUpdates(updateSession));
              }
            }
          });
    
          user.authenticate(password, function(authError, authenticated) {
            if (authError) {
              return done(authError);
            }
            if (!authenticated) {
              return done(null, false, { message: 'This password is not correct.' });
            } else {
              return done(null, user);
            }
          });
        })
        .catch(err => done(err));
    }
    
    export function setup(User, config) {
    
      passport.use(new LocalStrategy({
        passReqToCallback: true,
        usernameField: 'email',
        passwordField: 'password' // this is the virtual field on the model
      }, function(req, email, password, done) {
        return localAuthenticate(User, email, password, done, req);
      }));
    }
    
  3. 每次客户端发出请求时,都会触发isAuthenticated函数。这是我检查当前会话的seaAllowed布尔值的地方,如果为true,则允许用户访问该站点,否则注销用户:

    function saveUpdates(updates) {
      return function(entity) {
        var updated = _.merge(entity, updates);
        return updated.save()
          .then(updated => {
            return updated;
          });
      };
    }
    
    /**
     * Attaches the user object to the request if authenticated
     * Otherwise returns 403
     */
    export function isAuthenticated() {
    
      return compose()
        // Validate jwt
        .use(function(req, res, next) {
    
          // Allow access_token to be passed through query parameter as well
          if (req.query && req.query.hasOwnProperty('access_token')) {
            req.headers.authorization = 'Bearer ' + req.query.access_token;
          }
          validateJwt(req, res, next);
    
        })
        // Attach user to request
        .use(function(req, res, next) {
    
          User.findById(req.user._id).exec()
            .then(user => {
              if (!user) {
                return res.status(401).end();
              }
              req.user = user;
    
              ///////////////////////////
              // Login seat limitation //
              ///////////////////////////
    
              // Check if the user seat is allowed
              Sessions.findById(req.session.id).exec()
                .then(thisSession => {
                  // TODO access the session in a better way
                  if (thisSession.seatAllowed === false || thisSession.seatAllowed === undefined) {
                    res.redirect('/login');
                  }
              })
              next();
            })
            .catch(err => next(err));
        });
    }
    
  4. 多数民众赞成。