Passportjs挂在serializeUser上

时间:2018-09-03 21:24:03

标签: javascript node.js sequelize.js passport.js passport-local

我目前正在学习如何使用Passportjs和本地策略对用户进行身份验证,我一直在关注此教程:https://scotch.io/tutorials/easy-node-authentication-setup-and-local。我已经进行了一些更改,以使用sequelize代替猫鼬,现在登录时,我被重定向到一个空白错误页面。

控制台日志显示:

Login Requested
Executing (default): SELECT `id`, `localemail`, `localpassword`, 
`facebookid`, `facebooktoken`, `facebookname`, `facebookemail`, 
`twitterid`, `twittertoken`, `twitterdisplayname`, `twitterusername`, 
`googleid`, `googletoken`, `googleemail`, `googlename`, `createdAt`, 
`updatedAt` FROM `Users` AS `User` WHERE `User`.`localemail` = 
'test@test.co.uk' LIMIT 1;
User found and logged in: 6
Serializing User: 6
POST /login 302 118.674 ms - 60
Executing (default): SELECT `id`, `localemail`, `localpassword`, 
`facebookid`, `facebooktoken`, `facebookname`, `facebookemail`, 
`twitterid`, `twittertoken`, `twitterdisplayname`, `twitterusername`, 
`googleid`, `googletoken`, `googleemail`, `googlename`, `createdAt`, 
`updatedAt` FROM `Users` AS `User` WHERE `User`.`id` = 6;

我相信我已经将问题缩小到调用serializeUser函数的时间与页面呈现之前之间,这是我的护照配置文件:

const passport = require('passport');
const LocalStrategy = require('passport-local').Strategy;
const User = require('../models/user');
const bcrypt= require('bcrypt-nodejs');

passport.serializeUser(function(user, done) {
  console.log('Serializing User: ' + user.id);
  done(null, user.id);
});

passport.deserializeUser(function(id, done) {
  User.user.findOne({where: {id: id}}).then(function(err, user) {
    return done(err, user);
  }).catch(function(err) {
    return done(err);
  });
});

passport.use('local-signup', new LocalStrategy({
  usernameField: 'email',
  passwordField: 'password',
  passReqToCallback: true,
  },
  function(req, email, password, done) {
    process.nextTick(function() {
      User.user.findOne({where: {localemail: email}}).then(function(user) {
        if (user) {
          return done(null, false,
              req.flash('signupMessage', 'That email is already taken.'));
        } else {
          let newUser = new User.user();
          console.log(newUser);
          newUser.localemail = email;
          newUser.localpassword = User.generateHash(password);
          newUser.save().then(function(user) {
            return done(null, user);
          }).catch(function(err) {
            return done(err);
          });
        }
      }).catch(function(err) {
        return done(err);
      });
    });
  }
));

passport.use('local-login', new LocalStrategy({
  usernameField: 'email',
  passwordField: 'password',
  passReqToCallback: true,
  },
  function(req, email, password, done) {
    User.user.findOne({where: {localemail: email}}).then(function(user) {
      if (!user) {
        console.log('No User found!');
        return done(null, false, req.flash('loginMessage', 'No user found'));
      }
      if (!User.validPassword(password, user)) {
        console.log('Incorrect Password');
        return done(null, false, req.flash('loginMessage', 'Wrong password.'));
      }
      console.log('User found and logged in: ' + user.id);
      return done(null, user);
    }).catch(function(err) {
      return done(err);
    });
  }
));

module.exports = passport;

登录和成功重定向的路径:

router.post('/login', function(req, res, next) {
  console.log('Login Requested');
  next();
}, passport.authenticate('local-login', {
  successRedirect: '/profile',
  failureRedirect: '/login',
  failureFlash: true,
}));

无论尝试登录后进入哪个页面,在控制台中重复执行SQL查询以及空白错误页面,我都得到相同的结果。 我在stackoverflow上看到了很多issues similar to this,但是在尝试解决方案后没有任何效果。

更新

用户模型:

const Sequleize = require('sequelize');
const db = require('../config/database');
const bcrypt= require('bcrypt-nodejs');


let user = db.define('User', {
  localemail: Sequleize.STRING,
  localpassword: Sequleize.STRING,

  facebookid: Sequleize.STRING,
  facebooktoken: Sequleize.STRING,
  facebookname: Sequleize.STRING,
  facebookemail: Sequleize.STRING,

  twitterid: Sequleize.STRING,
  twittertoken: Sequleize.STRING,
  twitterdisplayname: Sequleize.STRING,
  twitterusername: Sequleize.STRING,

  googleid: Sequleize.STRING,
  googletoken: Sequleize.STRING,
  googleemail: Sequleize.STRING,
  googlename: Sequleize.STRING,
});

db.sync();

exports.validPassword = function(password, user) {
  return bcrypt.compareSync(password, user.localpassword);
};

exports.generateHash = function(password) {
  return bcrypt.hashSync(password, bcrypt.genSaltSync(8), null);
};

exports.user = user;

3 个答案:

答案 0 :(得分:0)

##设置护照登录而无需反序列化用户##

我认为您已经使用了2年前于2016年完成的折旧函数,事情的负载可能会在2年内发生变化,

假设您已经完成安装node.js的部分,请按照以下步骤重做。

为您的应用创建文件夹:

mkdir AuthApp cd AuthApp

创建节点应用程序: npm init

系统会提示您为Node的package.json提供一些信息。只需按Enter直到最后保留默认配置即可。

接下来,我们需要一个HTML文件发送给客户端。在应用程序的根文件夹中创建一个名为auth.html的文件,而不用太多的html进行测试:

<html>
<head>
<title>Node.js OAuth</title>
</head>
<body>
<a href=auth/facebook>Sign in with Facebook</a>
<br></br>
<a href=auth/github>Sign in with Github</a>
</body>
</html>

您还将需要Express,这是一个受Ruby的Sinatra启发而构建网络应用程序的框架。为了安装Express,从终端输入以下命令:

npm install express-保存 完成此操作后,就该编写一些代码了。

在应用程序的根文件夹中创建一个文件index.js,并向其中添加以下内容:

/*  EXPRESS SETUP  */
const express = require('express');
const app = express();
app.get('/', (req, res) => res.sendFile('auth.html', { root : __dirname}));
const port = process.env.PORT || 3000;
app.listen(port , () => console.log('App listening on port ' + port));

在上面的代码中,我们需要Express并通过调用express()创建Express应用。然后,我们声明应用首页的路线。在此处,我们将创建的HTML文件发送给访问该路由的客户端。然后,我们使用process.env.PORT将端口设置为环境端口变量(如果存在)。否则,我们将默认设置为3000,这是我们将在本地使用的端口。这为您提供了足够的灵活性,可以从开发直接切换到生产环境,在该生产环境中,端口可能由服务提供商(例如Heroku)设置。在下面,我们使用设置的port变量调用app.listen(),并通过简单的日志告诉我们它们一切正常,并且正在监听应用程序的端口。

现在,我们应该启动我们的应用程序,以确保所有功能均正常运行。只需在终端上编写以下命令:

节点index.js 您应该会看到以下消息:App正在监听3000端口。如果不是这种情况,则可能错过了一步。返回并重试。

继续,让我们看看是否将我们的页面提供给客户。转到网络浏览器并导航到http://localhost:3000

如果您可以看到我们在auth.html中创建的页面,那就很好了。

返回终端并使用ctrl + c停止应用程序。所以请记住,当我说启动应用程序时,编写节点index.js,而当我说停止应用程序时,则执行ctrl + c。明确?好,您刚刚被编程了:-)

设置护照 您很快就会意识到,Passport使为我们的用户提供身份验证变得轻而易举。让我们使用以下命令安装Passport:

npm安装护照-保存 现在我们必须设置Passport。在index.js文件的底部添加以下代码:

/*  PASSPORT SETUP  */

const passport = require('passport');
app.use(passport.initialize());
app.use(passport.session());
app.get('/success', (req, res) => res.send("You have successfully logged in"));
app.get('/error', (req, res) => res.send("error logging in"));

passport.serializeUser(function(user, cb) {
cb(null, user);
});

passport.deserializeUser(function(obj, cb) {
cb(null, obj);
});

在这里,我们需要Passport并将其连同其会话身份验证中间件一起直接在Express应用程序中进行初始化。然后,我们设置了'/ success'和'/ error'路由,这将呈现一条消息,告诉我们认证如何进行。这是我们上一条路线的语法,只是这次而不是使用res.SendFile(),而不是使用res.send(),这将在浏览器中将给定的字符串呈现为text / html。然后,我们使用serializeUser和deserializeUser回调。第一个将在身份验证时被调用,其工作是序列化用户实例并通过cookie将其存储在会话中。第二个请求将在随后的每个请求中被调用以反序列化该实例,并为其提供唯一的cookie标识符作为“凭据”。您可以在Passport文档中阅读有关此内容的更多信息。

作为一个附带说明,我们的这个非常简单的示例应用程序在没有deserializeUser的情况下也可以正常工作,但是它杀死了保持会话的目的,而这在每个需要登录的应用程序中都需要。

仅用于实际的Passport设置。现在我们终于可以开始业务了。

实施Facebook身份验证 为了提供Facebook身份验证,我们需要做的第一件事就是安装passport-facebook软件包。你知道怎么回事:

npm install passport-facebook --save

现在一切都已设置好,添加Facebook身份验证非常容易。在index.js文件的底部添加以下代码:

/*  FACEBOOK AUTH  */

const FacebookStrategy = require('passport-facebook').Strategy;
const FACEBOOK_APP_ID = 'your app id';
const FACEBOOK_APP_SECRET = 'your app secret';
passport.use(new FacebookStrategy({
clientID: FACEBOOK_APP_ID,
clientSecret: FACEBOOK_APP_SECRET,
callbackURL: "/auth/facebook/callback"
},
function(accessToken, refreshToken, profile, cb) {
  return cb(null, profile);
}
));

app.get('/auth/facebook',
passport.authenticate('facebook'));

app.get('/auth/facebook/callback',
passport.authenticate('facebook', { failureRedirect: '/error' }),
function(req, res) {
res.redirect('/success');
});

让我们逐步完成此代码块。首先,我们需要passport-facebook模块。然后,我们声明用于存储应用ID和应用密钥的变量(我们将很快了解如何获取这些变量)。之后,我们告诉Passport使用所需的FacebookStrategy实例。为了实例化上述策略,我们为它提供了应用ID和应用机密变量以及用于验证用户身份的callbackURL。作为第二个参数,它使用一个函数,该函数将返回用户提供的配置文件信息。

进一步,我们设置了路由以提供身份验证。如您在callbackURL中所见,我们将用户重定向到我们之前定义的/ error和/ success路由。我们使用的是password.authenticate,它会尝试使用第一个参数(在本例中为facebook)的给定策略进行身份验证。您可能已经注意到我们做了两次。在第一个应用程序上,它将请求发送到我们的Facebook应用程序。第二个是由回调URL触发的,Facebook将使用该URL来响应登录请求。

现在,您需要创建一个Facebook应用。有关如何执行此操作的详细信息,请参阅Facebook非常详细的指南“创建Facebook应用程序”,其中提供了有关如何创建应用程序的逐步说明。

创建应用程序后,请转到应用程序配置页面上的“设置”。在这里,您会看到自己的应用程序ID和应用程序密码。不要忘记使用相应的值更改在index.js文件中为它们声明的变量。

接下来,在“应用程序域”字段中输入“ localhost”。然后,转到页面底部的“添加平台”,然后选择“网站”。使用http://localhost:3000/auth/facebook/callback作为网站URL。

在左侧边栏的“产品”部分下,您应该看到Facebook登录。点击进入那里。

最后,将有效OAuth重定向URI字段设置为http://localhost:3000/auth/facebook/callback

如果立即启动应用程序并单击“使用Facebook登录”链接,Facebook会提示您提供所需的信息,并且登录后,应将您重定向到/ success路由,在该路由中会显示消息您已成功登录。

就是这样!您刚刚设置了Facebook身份验证。很简单,对吧?

实施GitHub身份验证 添加GitHub身份验证的过程与我们对Facebook所做的非常相似。首先,我们将安装Passport-github模块:

npm install passport-github --save

现在转到index.js文件,并在底部添加以下行:

/*  GITHUB AUTH  */

const GitHubStrategy = require('passport-github').Strategy;

const GITHUB_CLIENT_ID = "your app id"
const GITHUB_CLIENT_SECRET = "your app secret";

passport.use(new GitHubStrategy({
clientID: GITHUB_CLIENT_ID,
clientSecret: GITHUB_CLIENT_SECRET,
callbackURL: "/auth/github/callback"
},
function(accessToken, refreshToken, profile, cb) {
  return cb(null, profile);
}
));

app.get('/auth/github',
passport.authenticate('github'));

app.get('/auth/github/callback',
passport.authenticate('github', { failureRedirect: '/error' }),
function(req, res) {
res.redirect('/success');
});

这看起来很熟悉!几乎和以前一样。唯一的区别是我们使用的是GithubStrategy而不是FacebookStrategy。

到目前为止,……相同。如果您还没有弄清楚,下一步就是创建我们的GitHub App。 GitHub有一个非常简单的指南,即创建GitHub应用程序,它将指导您完成该过程。

完成后,就像在Facebook上一样,您需要在配置面板中将主页URL设置为http://localhost:3000/,将授权回调URL设置为http://localhost:3000/auth/github/callback

现在,只需重启Node服务器并尝试使用GitHub链接登录。

“护照设置”过程结束。

本问题涵盖了存在问题的序列化用户的

护照  教程链接click here to view it

答案 1 :(得分:0)

可以向模型添加两种类型的方法-1.实例方法和2.类方法。这里的更多内容=> https://medium.com/@benjaminconant/defining-instance-methods-on-sequelize-js-models-dea36f478950

您可以像这样将validatePasswordgenerateHash方法指定为User模型的类方法

const Sequleize = require('sequelize');
const db = require('../config/database');
const bcrypt= require('bcrypt-nodejs');


let User = db.define('User', {
  localemail: Sequleize.STRING,
  localpassword: Sequleize.STRING,

  facebookid: Sequleize.STRING,
  facebooktoken: Sequleize.STRING,
  facebookname: Sequleize.STRING,
  facebookemail: Sequleize.STRING,

  twitterid: Sequleize.STRING,
  twittertoken: Sequleize.STRING,
  twitterdisplayname: Sequleize.STRING,
  twitterusername: Sequleize.STRING,

  googleid: Sequleize.STRING,
  googletoken: Sequleize.STRING,
  googleemail: Sequleize.STRING,
  googlename: Sequleize.STRING,
}, {
    classMethods: {
        validPassword: function(password, user) {
            return bcrypt.compareSync(password, user.localpassword);
        };

        generateHash: function(password) {
            return bcrypt.hashSync(password, bcrypt.genSaltSync(8), null);
        };
    }    
});

module.exports = User;

现在,您可以像这样使用User模型

passport.deserializeUser(function(id, done) {
  User.findOne({where: {id: id}}).then(function(err, user) {
    return done(err, user);
  }).catch(function(err) {
    return done(err);
  });
});

passport.use('local-signup', new LocalStrategy({
  usernameField: 'email',
  passwordField: 'password',
  passReqToCallback: true,
  },
  function(req, email, password, done) {
    process.nextTick(function() {
      User.findOne({where: {localemail: email}}).then(function(user) {
        if (user) {
          return done(null, false,
              req.flash('signupMessage', 'That email is already taken.'));
        } else {
          let newUser = new User();
          console.log(newUser);
          newUser.localemail = email;
          newUser.localpassword = User.generateHash(password);
          newUser.save().then(function(user) {
            return done(null, user);
          }).catch(function(err) {
            return done(err);
          });
        }
      }).catch(function(err) {
        return done(err);
      });
    });
  }
)); 

passport.use('local-login', new LocalStrategy({
  usernameField: 'email',
  passwordField: 'password',
  passReqToCallback: true,
  },
  function(req, email, password, done) {
    User.findOne({where: {localemail: email}}).then(function(user) {
      if (!user) {
        console.log('No User found!');
        return done(null, false, req.flash('loginMessage', 'No user found'));
      }
      if (!User.validPassword(password, user)) {
        console.log('Incorrect Password');
        return done(null, false, req.flash('loginMessage', 'Wrong password.'));
      }
      console.log('User found and logged in: ' + user.id);
      return done(null, user);
    }).catch(function(err) {
      return done(err);
    });
  }
));

module.exports = passport;

答案 2 :(得分:0)

问题不在于passportjs,而是我在deserializeUser方法中的续集查询:

User.user.findOne({where: {id: id}}).then(function(err, user) { ...

查询仅返回一个传递给.then()函数的变量,因此将其更改为:

User.user.findOne({where: {id: id}}).then(function(user) { ..

修复了我的错误。