护照本地策略未被调用

54

我确定我在这里漏掉了一些非常明显的东西,但是我无法弄清楚。我已经传递给LocalStrategy构造函数的函数在登录表单提交时不会被调用。

代码:

var express = require('express');
var http = require('http');
var path = require('path');
var swig = require('swig');
var passport = require('passport');

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

passport.serializeUser(function(user, done) {
  console.log('Serialize user called.');
  done(null, user.name);
});

passport.deserializeUser(function(id, done) {
  console.log('Deserialize user called.');
  return done(null, {name: 'Oliver'});
});

passport.use(new LocalStrategy(
  function(username, password, done) {
    console.log('local strategy called with: %s', username);
    return done(null, {name: username});
  }));

var app = express();

app.set('port', process.env.PORT || 3000);
app.set('view engine', 'swig');
app.set('views', __dirname + '/views');
app.use(express.favicon());
app.use(express.logger('dev'));
app.use(express.bodyParser());
app.use(express.methodOverride());
app.use(express.cookieParser('asljASDR2084^^!'));
app.use(express.session());
app.use(passport.initialize());
app.use(passport.session());
app.use(app.router);
app.use(require('less-middleware')({ src: __dirname + '/public' }));
app.use(express.static(path.join(__dirname, 'public')));
app.use(express.errorHandler({ dumpExceptions:true, showStack:true }));

app.engine('swig', swig.renderFile);

app.post('/auth', passport.authenticate('local'));
app.get('/login', function(req, res) {
  // login is a simple form that posts username and password /auth
  res.render('login', {});
});

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

我在我的/login页面上使用了从passport文档中剪切和粘贴的表单,只是将它改为提交到/auth而不是/login:

<form action="/auth" method="post">
    <div>
        <label>Username:</label>
        <input type="text" name="username"/>
    </div>
    <div>
        <label>Password:</label>
        <input type="password" name="password"/>
    </div>
    <div>
        <input type="submit" value="Log In"/>
    </div>
</form>
当我提交登录表单时,以下内容被记录。
GET /login 200 5ms - 432b
POST /auth 401 6ms
请注意,“local strategy called”永远不会被记录。为什么我传递给LocalStrategy的函数没有被调用?
16个回答

92

我最近遇到了这个问题,可能由许多因素引起。首先要检查的是确保为express设置了bodyParser,我看到你已经做了这一点。


app.use(express.bodyParser());

下一步是确保您提交给路由的表单包含usernamepassword并且用户必须在这两个字段中都输入内容。我在测试时曾经因为没有在密码字段中输入任何内容而陷入了困境 :) Passport要求同时提供这两个字段才能执行传递给passport.authenticate('local')的LocalStrategy验证函数。

您的示例似乎也已正确设置以捕获用户名和密码,但无论如何,您应该尝试测试是否即使没有使用passport也可以正确解析请求体:

app.post('/auth', function(req, res){
  console.log("body parsing", req.body);
  //should be something like: {username: YOURUSERNAME, password: YOURPASSWORD}
});

否则

您是否尝试为您的 /auth 路由添加请求处理程序?

app.post('/auth', passport.authenticate('local'), function(req, res){
  console.log("passport user", req.user);
});
或者
app.post('/auth', passport.authenticate('local', { successRedirect: '/', failureRedirect: '/auth' }));

1
当然!我觉得一开始我在使用它时遇到了一些困难,因为我没有完全理解它,但是一旦它运行起来,它就是一个非常有用的库。 - Arjun Mehta
7
我刚刚遇到了“双字段必填”的问题,非常感谢你指出来,我已经因此浪费了一个小时的时间,而且可能会更久。 - TRayburn
谢谢!经过数小时的苦思冥想,终于让我找到了解决方法。添加bodyParser对我很有帮助。 - Levi Roberts
@)TRayburn,@)arjun 同上! - bitcruncher
5
如果您正在使用最新版本,则bodyParser()已被弃用。我使用了bodyParser.urlencoded({extended:true}),它完全可以正常工作。 - payne8
显示剩余3条评论

46

当我设置自己的路由处理程序并从中调用passport.authenticate()时,我遇到了同样的问题。我忘记了passport.authenticate返回必须被调用的中间件,所以仅仅调用passport.authenticate是不够的。

然后我进行了替换。

router.post("/",
 function(req,res,next){
   passport.authenticate("local", function(err, user, info){

    // handle succes or failure

  }); 
})

随着

router.post("/",
 function(req,res,next){
   passport.authenticate("local", function(err, user, info){

    // handle succes or failure

  })(req,res,next); 
})

7
感谢让我意识到 passport.authenticate() 返回了一个函数! - jamesjansson
谢谢,我尝试了您的代码。它可以工作,但是我找不到req.body或者req.id。我的方法路由是get(router.get('/', ...))。 - Wicak
太好了,我从示例中复制了代码,但错过了这个小细节(尽管我已经多次使用了Express和Passport)。谢谢你! - Martin Sjöblom
我遇到了相同的问题,但是使用的是Koa而不是express。必须将Koa上下文作为参数传递给返回的函数。 - Nice Books

26

如果您使用的是与默认输入字段不同的用户名和密码输入字段,则会出现401错误。您必须通过以下方式在LocalStrategy中提供:

passport.use(new LocalStrategy({
    usernameField: 'login',
    passwordField: 'password'
  },

  function(username, password, done) {
  ...
  }
));

默认是用户名密码,我觉得。查看这里的文档。


谢谢你的建议,但我不认为那是问题所在。我实际上是将表单从那些文档中剪切并粘贴到我的登录页面中,所以我相当确定我已经正确命名了字段。我会更新我的问题以包含这些信息。 - Oliver Dain
3
我目前遇到了困难,我发现在本地护照中间件中,中间件正在查看req并尝试查看req.body,但是当我使用console.log时它是未定义的 - 这会返回“错误的消息错误”,永远无法为我的post解决问题。基本上身份验证甚至没有发生。 - Catalyst
1
@Catalyst req.body 应该由 bodyParser 中间件设置。 - Julien Bérubé
1
啊!非常感谢!我有一个电子邮件表单。他们应该更明显地提到它,或者至少管理文档,以便立即提供额外的对象来设置这些参数,而不是使用默认实现。 - Tommz
这应该被标记为正确答案!!! - Doctiger

10

我认为您提交的表单中用户名或密码字段为空。

如果您填写了两个输入框并提交,LocalStrategy 将被调用。


是的。看起来我已经取得了进展。我只是在尝试用户名字段时,没有包含密码。一旦我输入了密码,我就看到了进展。这太令人沮丧了,因为我并不在意密码。我只是在测试东西。我不知道它是必需的。 - jack blank

8

对于Express 4.x:

// Body parser
var bodyParser = require('body-parser');

app.use(bodyParser.urlencoded({ extended: false })) // parse application/x-www-form-urlencoded
app.use(bodyParser.json()) // parse application/json

参考 https://github.com/expressjs/body-parser


5
我也因为同样的问题头疼了几个小时。像其他答案说的那样,我已经把所有东西都放在了正确的位置,比如: 1. 表单返回了所有字段。 2. body parser被设置正确,并启用了extended:true。 3. Passport设置和配置都正确。
但是,我将用户名字段更改为手机号和密码。通过像下面这样更改代码解决了我的问题。
  passport.use('login', new LocalStrategy(
    {usernameField:'phoneNumber',
    passwordField:'password'},
    function (username, password, done) {

    // your login goes here
    })

如果有人需要,我可以编写使用mongodb和passport-local的手机号身份验证过程。

感谢@user568109


4

我的问题是,我在enctype='multipart/form-data'。只需要将其更改为multipart='urlencoded',我认为这样就可以解决它。


1
谢谢!我本来要从头开始写我的认证系统,但现在不用了。不知为何,这个包组合的Passport "passport": "^0.3.2", "passport-http": "^0.3.0", "passport-local": "^1.0.0", 在登录表单中使用 enctype='multipart/form-data' 属性时无法正常工作。 - larrydalmeida

2
为了捕获用户名和密码,请确保添加以下内容:

usernamepassport

app.use(express.urlencoded());

在我的案例中,我使用了app.use(express.json());并从<form>提交了用户名和密码。

1
确保在表单中将您的传递名称命名为用户名
<form action="/user/user-session" method="post">
    <input type="email" name="username" placeholder="Enter your email address" required>
    <input type="password" name="password" placeholder="password">
    <input type="submit" value="Sign in">
</form>

0

可能是您的请求格式不正确(特别是请求正文),导致您认为已经发送了用户名和密码,但实际上并没有发送。

以下是一个API调用包装器的示例,它强制将您的请求正文解析为json格式:

Api = {};
Api.request = (route, options) => {
  options = options || {};

  options.method = options.method || 'GET';
  options.credentials = 'include';

  if (options.method === 'POST') {
    options.headers = {
      'Accept': 'application/json',
      'Content-Type': 'application/json',
    };
    options.body = JSON.stringify(options.data) || '{}';
  }

  fetch(absoluteUrl + '/api/' + route, options)
    .then((response) => response.json())
    .then(options.cb || (() => {}))
    .catch(function(error) {
      console.log(error);
    });
};

它可以这样使用:

Api.request('login', {
  data: {
    username: this.state.username,
    password: this.state.password
  },
  method: 'POST',
  cb: proxy((user) => {
    console.log(user);
  }, this)
});

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