首页 > 解决方案 > Passportjs 挂在 serializeUser 上

问题描述

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

控制台日志显示:

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上看到了很多类似的问题但是 尝试了解决方案之后没有任何效果。

更新

用户型号:

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;

标签: javascriptnode.jssequelize.jspassport.jspassport-local

解决方案


## 设置 Passport Login swithout deserializeUser ##

我认为您已经使用了 2016 年 2 年前完成的教程的一些折旧功能,2 年内很多事情都会发生变化,

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

为您的应用程序制作文件夹:

mkdir AuthApp cd AuthApp

创建节点应用程序:npm init

系统将提示您提供有关 Node 的 package.json 的一些信息。只需按回车键直到结束即可离开默认配置。

接下来,我们需要一个 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 启发的 Web 应用程序的框架。为了安装 Express,从终端输入以下命令:

npm install express --save 完成后,就该编写一些代码了。

在您的应用程序的根文件夹中创建一个文件 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)设置。在下面,我们使用我们设置的端口变量调用 app.listen() 和一个简单的日志,让我们知道一切正常,以及应用程序正在侦听哪个端口。

现在我们应该启动我们的应用程序以确保一切正常。只需在终端上编写以下命令:

node index.js 你应该看到消息:App正在监听端口3000。如果不是这样,你可能错过了一步。回去再试一次。

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

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

返回终端并使用 ctrl + c 停止应用程序。所以请记住,当我说启动应用程序时,您编写 node index.js,当我说停止应用程序时,您执行 ctrl + c。清除?很好,你刚刚被编程:-)

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

npm install passport --save 现在我们必须设置 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.send()而不是使用res.SendFile( ),它将在浏览器中将给定的字符串呈现为 text/html。然后我们使用 serializeUser 和 deserializeUser 回调。第一个将在身份验证时调用,它的工作是序列化用户实例并通过 cookie 将其存储在会话中。第二个将在每个后续请求中调用以反序列化实例,并为其提供唯一的 cookie 标识符作为“凭据”。您可以在 Passport 文档中阅读更多相关信息。

附带说明一下,我们的这个非常简单的示例应用程序在没有反序列化用户的情况下也可以正常工作,但它破坏了保持会话的目的,这是您在每个需要登录的应用程序中都需要的东西。

这就是实际 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 实例。为了实例化上述策略,我们给它我们的 app id 和 app secret 变量以及我们将用来验证用户身份的 callbackURL。作为第二个参数,它采用一个函数,该函数将返回用户提供的配置文件信息。

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

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

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

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

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

最后,将 Valid OAuth redirect URIs 字段设置为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 应用程序。GitHub 有一个非常简单的指南,即创建 GitHub 应用程序,它将指导您完成整个过程。

完成后,在配置面板中,您需要将主页 URL 设置为http://localhost:3000/并将授权回调 URL 设置为http://localhost:3000/auth/github/callback,就像我们用 Facebook 做到了。

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

护照设置过程结束。

本教程链接也涵盖了存在问题的序列化用户的护照 单击此处查看


推荐阅读