NodeJs Passport isAuthenticated() 即使在登录后也返回 false

Posted

技术标签:

【中文标题】NodeJs Passport isAuthenticated() 即使在登录后也返回 false【英文标题】:NodeJs Passport isAuthenticated() returning false even after login 【发布时间】:2014-12-23 12:45:08 【问题描述】:

我是 Angular.js 的新手,正在尝试为网站构建本地身份验证。我浏览了各种来源,Authentication in Single Page Applications 非常有帮助。当我尝试在本地主机中构建相同的代码时,我的代码进入了循环。

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]);
  );
);

【问题讨论】:

嘿!我也遇到了同样的问题,你找到根本原因了吗? 这个例子对我来说有点奇怪,因为我希望 /loggedin 的请求以某种方式签名,至少使用 $httpProvider.defaults.withCredentials = true;,否则我不确定请求是否包含任何引用您的登录用户。 快速思考:>> angular session != express session >> 当 angular 调用 '/isloggedin' 时,它正在发送它的请求会话以表达未经过身份验证的应用程序添加日志,如果你可以分享这些知道什么是 req 对象等会很有帮助,那么我们可能可以更快地解决这个问题:) 【参考方案1】:

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

【讨论】:

为什么浏览器不保留cookie?这应该是它的默认行为! 发生这种情况的原因有很多:例如,设置 cookie 时设置了过期时间。【参考方案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() 函数。

【讨论】:

【参考方案3】:

可以有很多东西。

1.) 像PassportJS Facebook login isAuthenticated returns false even though authentication succeeds 中的顺序(尽管在您的情况下顺序似乎正确)。

2.) 没有 req.login() 就像Passport and Passport Local req.isAuthenticated always returns false

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

【讨论】:

【参考方案4】:

您需要允许跨域设置cookies

快递

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

在 ajax 设置中

 xhrFields: 
     withCredentials: true
 

您可以在here和here找到相关答案

【讨论】:

【参考方案5】:

忘记添加也遇到了同样的问题

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);
    
);

还要确保您有以下order 用于初始化

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());

【讨论】:

【参考方案6】:

在我的情况下,我尝试了 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);

【讨论】:

以上是关于NodeJs Passport isAuthenticated() 即使在登录后也返回 false的主要内容,如果未能解决你的问题,请参考以下文章

在获取用户时使用 Passport-jwt 时 req.user 未定义

什么是passport.initialize()? (nodejs快递)

用户未定义:Nodejs/Express + Passport

NestJS / NodeJS / Passport / JWT - 股票当前用户

Nodejs + Passport.js + Redis:如何在 Redis 中存储会话

NodeJs:错误401(未经授权)我正在使用passport-jwt