NodeJs Passport登录后isAuthenticated()返回false。

19

我刚开始接触Angular.js,正在尝试为一个网站构建本地验证。我已经查阅了各种资料,单页应用程序中的验证非常有帮助。但是,当我在本地主机上构建相同的内容时,我的代码陷入了循环。

app.post('/login',.....) 返回响应中的用户,但之后在加载管理页面时,它通过调用app.get('/loggedin',... )检查用户是否已登录,并且即使登录了req.isAuthenticated()也会返回false并进入循环。我无法理解为什么会发生这种情况,请帮帮我。

服务器端代码

var express = require('express');
var http = require('http');
var path = require('path');
var passport = require('passport');
var LocalStrategy = require('passport-local').Strategy;

//==================================================================
// Define the strategy to be used by PassportJS
passport.use(new LocalStrategy(
  function(username, password, done) {
    if (username === "admin" && password === "admin") // stupid example
      return done(null, {name: "admin"});

    return done(null, false, { message: 'Incorrect username.' });
  }
));

// Serialized and deserialized methods when got from session
passport.serializeUser(function(user, done) {
    done(null, user);
});

passport.deserializeUser(function(user, done) {
    done(null, user);
});

// Define a middleware function to be used for every secured routes
var auth = function(req, res, next){
  if (!req.isAuthenticated()) 
    res.send(401);
  else
    next();
};
//==================================================================

// Start express application
var app = express();

// all environments
app.set('port', process.env.PORT || 3000);
app.use(express.favicon());
app.use(express.cookieParser()); 
app.use(express.bodyParser());
app.use(express.methodOverride());
app.use(express.session({ secret: 'securedsession' }));
app.use(passport.initialize()); // Add passport initialization
app.use(passport.session());    // Add passport initialization
app.use(app.router);

app.all('*', function(req, res, next) {
  res.header("Access-Control-Allow-Origin", "*");
  res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
  next();
});

// development only
if ('development' == app.get('env')) {
  app.use(express.errorHandler());
}

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

app.get('/users', auth, function(req, res){
  res.send([{name: "user1"}, {name: "user2"}]);
});
//==================================================================

//==================================================================
// route to test if the user is logged in or not
app.get('/loggedin', function(req, res) {
  res.send(req.isAuthenticated() ? req.user : '0');
});

// route to log in
app.post('/login', passport.authenticate('local'), function(req, res) {
  res.send(req.user);
});

// route to log out
app.post('/logout', function(req, res){
  req.logOut();
  res.send(200);
});
//==================================================================

http.createServer(app).listen(app.get('port'), function(){
  console.log('Express server listening on port ' + app.get('port'));
});

客户端JS文件

'use strict';

/**********************************************************************
 * Angular Application
 **********************************************************************/
var app = angular.module('app', ['ngResource','ngRoute'])
  .config(function($routeProvider, $locationProvider, $httpProvider) {
    //================================================
    // Check if the user is connected
    //================================================
    var checkLoggedin = function($q, $timeout, $http, $location, $rootScope){
      // Initialize a new promise
      var deferred = $q.defer();

      // Make an AJAX call to check if the user is logged in
      $http.get('http://localhost:3000/loggedin').success(function(user){
        // Authenticated
        if (user !== '0')
          $timeout(deferred.resolve, 0);

        // Not Authenticated
        else {
          $rootScope.message = 'You need to log in.';
          $timeout(function(){deferred.reject();}, 0);
          $location.url('/login');
        }
      });

      return deferred.promise;
    };
    //================================================

    //================================================
    // Add an interceptor for AJAX errors
    //================================================
    $httpProvider.responseInterceptors.push(function($q, $location) {
      return function(promise) {
        return promise.then(
          // Success: just return the response
          function(response){
            return response;
          }, 
          // Error: check the error status to get only the 401
          function(response) {
            if (response.status === 401)
              $location.url('/login');
            return $q.reject(response);
          }
        );
      }
    });
    //================================================

    //================================================
    // Define all the routes
    //================================================
    $routeProvider
      .when('/', {
        templateUrl: 'views/main.html'
      })
      .when('/admin', {
        templateUrl: 'views/admin.html',
        controller: 'AdminCtrl',
        resolve: {
          loggedin: checkLoggedin
        }
      })
      .when('/login', {
        templateUrl: 'views/login.html',
        controller: 'LoginCtrl'
      })
      .otherwise({
        redirectTo: '/login'
      });
    //================================================

  }) // end of config()
  .run(function($rootScope, $http){
    $rootScope.message = '';

    // Logout function is available in any pages
    $rootScope.logout = function(){
      $rootScope.message = 'Logged out.';
      $http.post('http://localhost:3000/logout');
    };
  });


/**********************************************************************
 * Login controller
 **********************************************************************/
app.controller('LoginCtrl', function($scope, $rootScope, $http, $location) {
  // This object will be filled by the form
  $scope.user = {};

  // Register the login() function
  $scope.login = function(){
    $http.post('http://localhost:3000/login', {
      username: $scope.user.username,
      password: $scope.user.password,
    })
    .success(function(user){
      // No error: authentication OK
      $rootScope.message = 'Authentication successful!';
      $location.url('/admin');
    })
    .error(function(){
      // Error: authentication failed
      $rootScope.message = 'Authentication failed.';
      $location.url('/login');
    });
  };
});



/**********************************************************************
 * Admin controller
 **********************************************************************/
app.controller('AdminCtrl', function($scope, $http) {
  // List of users got from the server
  $scope.users = [];

  // Fill the array to display it in the page
  $http.get('http://localhost:3000/users').success(function(users){
    for (var i in users)
      $scope.users.push(users[i]);
  });
});

嘿!我也遇到了同样的问题,你找到根本原因了吗? - Ben Orozco
这个例子对我来说有点奇怪,因为我期望对/loggedin的请求在某种程度上被签名,至少通过$ httpProvider.defaults.withCredentials = true;,否则我不确定该请求是否包含任何关于已登录用户的引用。 - pasine
快速想法:
Angular 会话 != Express 会话 当 Angular 调用 '/isloggedin' 时,它会将请求会话发送到未经身份验证的 Express 应用程序
添加日志并共享这些日志将有助于了解 req 对象等信息,然后我们可能可以更快地解决这个问题 :)
- drunkenRabbit
6个回答

8

您需要允许在跨域中设置Cookie

在Express中

 res.header('Access-Control-Allow-Credentials', true);

在Ajax设置中

 xhrFields: {
     withCredentials: true
 }

你可以在这里这里找到相关答案。


2

我认为rdegges的想法部分正确,因为cookie和会话变量是使状态管理工作的一部分。我认为bodyParser也是必需的,但我在这里省略了它。

我在我的网站上使用Passport(对MongoDB用户表进行身份验证),以下是我代码的摘录。

/server.js:

var cookieParser = require('cookie-parser');
...
var passport = require('passport');
var expressSession = require('express-session');
var initPassport = require('./passport/init');
initPassport(passport);
...
self.app.use(cookieParser());
self.app.use(expressSession({secret: 'MYSECRETISVERYSECRET', saveUninitialized: true, resave: true}));
self.app.use(passport.initialize());
self.app.use(passport.session());
...
var routes = require('./routes/index')(passport);
self.app.use('/', routes);

/passport/init.js:

var login = require('./login');
var signup = require('./register');
var User = require('../models/user');

module.exports = function(passport) {
  // Passport needs to be able to serialize and deserialize users to support persistent login sessions
  passport.serializeUser(function(user, done) {
    console.log('serializing user: ');
    console.log(user);
    done(null, user._id);
  });

  passport.deserializeUser(function(id, done) {
    User.findById(id, function(err, user) {
      console.log('deserializing user:', user);
      done(err, user);
    });
  });

  // Setting up Passport Strategies for Login and SignUp/Registration
  login(passport);
  signup(passport);
}

/routes/index.js:

var passport = require('passport');
var User = require('../models/user');
...
var isAuthenticated = function (req, res, next) {
  // if user is authenticated in the session, call the next() to call the next request handler 
  // Passport adds this method to request object. A middleware is allowed to add properties to
  // request and response objects
  if (req.isAuthenticated())
    return next();
  // if the user is not authenticated then redirect him to the login page
  res.redirect('/login');
}

就我所知,您的isValidated()函数未在任何地方定义。

1

可以有很多种情况。

1.) 如 PassportJS Facebook登录isAuthenticated返回false,尽管身份验证成功(在您的情况下似乎顺序正确)。

2.) 没有req.login(),如Passport和Passport Local req.isAuthenticated始终返回false

在这种情况下,我选择后者,但原因与该问题不同。 您提供了自己的LocalStrategy。 要使用户登录,您将不得不自己调用req.login()。 就像您定义自己的自定义回调函数一样,如passport文档中所述:http://passportjs.org/guide/authenticate/


1
我遇到了同样的问题,因为忘记添加


request.login()

在。
app.post('/login', 
    function(request, response, next) {
        console.log(request.session)
        passport.authenticate('login', 
        function(err, user, info) {
            if(!user){ response.send(info.message);}
            else{

                request.login(user, function(error) {
                    if (error) return next(error);
                    console.log("Request Login supossedly successful.");
                    return response.send('Login successful');
                });
                //response.send('Login successful');
            }

        })(request, response, next);
    }
);

还要确保您具有以下顺序以进行初始化

var session = require('express-session');

// required for passport session
app.use(session({
  secret: 'secrettexthere',
  saveUninitialized: true,
  resave: true,
  // using store session on MongoDB using express-session + connect
  store: new MongoStore({
    url: config.urlMongo,
    collection: 'sessions'
  })
}));

// Init passport authentication 
app.use(passport.initialize());
// persistent login sessions 
app.use(passport.session());

0

你的浏览器是否保留了你的会话cookie?在我看来,你的浏览器在登录后没有保持你的会话cookie,这就是为什么对/loggedin的后续请求失败的原因。


为什么浏览器不会保留cookie?这应该是它的默认行为! - Ben Orozco
这种情况可能有很多原因:例如,当设置 cookie 时,它会带有过期时间。 - rdegges

0
在我的情况下,我尝试了JMeas建议的手动调用会话保存的解决方案,但它没有起作用。

https://github.com/jaredhanson/passport/issues/482

req.session.save(function() { successRedirect(); })

经过一些实验,我将 app.use(session({ ... })) 移到所有中间件调用的顶部,现在 req.isAuthenticated() 的工作正常。我认为,会话设置应该作为第一个中间件或至少在设置 cookie 之前进行。
错误调用:
var app = express();

app.use(query.json());
app.use(query.urlencoded({ extended: false }));
app.use(cookies());
app.use(express.static(path.join(__dirname, 'public')));
app.use(passport.initialize());
app.use(passport.session());

app.use(session({
    secret: 'card',
    resave: true,
    saveUninitialized: true
}));

app.use('/', routes); // this is where I call passport.authenticate()

修复调用:

app.use(session({
    secret: 'card',
    resave: true,
    saveUninitialized: true
}));

app.use(query.json());
app.use(query.urlencoded({ extended: false }));
app.use(cookies());
app.use(express.static(path.join(__dirname, 'public')));
app.use(passport.initialize());
app.use(passport.session());

app.use('/', routes);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接