包含用于在 passport-local 中注册的电子邮件的输入字段是否必须具有属性 'name = username'?

Does input field containing email for signup in passport-local have to have attribute 'name = username'?

我设置了本地通行证策略来登录和注册用户,我当前用户流程的工作方式是用户访问注册页面,输入他们的电子邮件地址和密码,然后可以使用完全相同的凭据登录在登录视图中。没有什么花哨。

我注意到,当我尝试将输入字段的单行名称属性从 'username' 更改为类似 'email' 以使其更具可读性时,代码中断消息 "Invalid user credentials" 通过 connect-flash 传播。我见过的每一个概念证明都有输入信息 'username',但是当我更改那一行并输入一个不同的新电子邮件时,注册不再有效。

这里是有问题的观点:

views/signup.ejs

<h1><span class="fa fa-sign-in"></span> Signup</h1>

<!-- show any messages that come back with authentication -->
<% if (message.length > 0) { %>
    <div class="alert alert-danger"><%= message %></div>
<% } %>

<!-- LOGIN FORM -->
<form action="/users/signup" method="post">
    <div class="form-group">
        <label>Email</label>
        <!-- the line directly below works -->
        <input type="text" class="form-control" name="username">
        <!-- but this line immediately below does not work! -->
        <!-- <input type="text" class="form-control" name="email"> -->
    </div>
    <div class="form-group">
        <label>Password</label>
        <input type="password" class="form-control" name="password">
    </div>

    <button type="submit" class="btn btn-warning btn-lg">Signup</button>
</form>

这是我的本地注册策略:

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

passport.deserializeUser(function(id, done) {
    db.User.findById(id).then(function(user) {
        done(null, user);
    })
    .catch(function(err){
        done(err, null);
    });
});

passport.use('local-signup', new LocalStrategy({
    emailField : 'email',
    passwordField : 'password',
    passReqToCallback : true }, 
    function(req, email, password, done){
        if(req.session.user && req.session){
            done(null, false, {message: 'User is already logged in, signup illegal'});
        }
        else{
            db.User.findOne({ where: {email : email }})
                .then(function(user) {
                 if(user !== null) {
                     done(null, false, {message: 'User already exists'});
                 }   
                 else{
                     //create user yada yada
                 }
            })
            .catch(function(err){
                 return done(null, false, {message: err});
            });    
         }
     }
));

passport.js 是否绝对要求注册视图中注册策略的属性必须是用户名?这让我觉得非常奇怪。再次强调,我对现有功能注册流程所做的唯一更改实际上是将属性更改为 'email' 而不是 'username'.

虽然类似的问题已经被问过几次,但在 PassportJS 使身份验证变得简单的背景下,他们没有一个很好的答案,所以我们不能指望实施它的人深入研究它代码。

Passport-local 是大多数人在使用 PassportJS 时会使用的策略。如上所述,它通常需要用户名和电子邮件的值。

如果您进入 node_modules/<path to passport-local>/strategy.js,您会找到更多关于如何实现它的文档。

`Strategy` constructor.

The local authentication strategy authenticates requests based on the
credentials submitted through an HTML-based login form.

Applications must supply a `verify` callback which accepts `username` and
`password` credentials, and then calls the `done` callback supplying a
`user`, which should be set to `false` if the credentials are not valid.
If an exception occured, `err` should be set.

Optionally, `options` can be used to change the fields in which the
credentials are found.

Options:
  - `usernameField`  field name where the username is found, defaults to _username_
  - `passwordField`  field name where the password is found, defaults to _password_
  - `passReqToCallback`  when `true`, `req` is the first argument to the verify callback (default: `false`)

下面提供的 Jared Hanson 的示例坦率地说是不充分的,因为它甚至没有实现任何这些功能,因此,我省略了它,但是如果您查看实际的 Strategy 构造函数的代码,清晰多了。

    function Strategy(options, verify) {
      if (typeof options == 'function') {
        verify = options;
        options = {};
      }
      if (!verify) { throw new TypeError('LocalStrategy requires a verify callback'); }

      this._usernameField = options.usernameField || 'username';
      this._passwordField = options.passwordField || 'password';

      passport.Strategy.call(this);
      this.name = 'local';
      this._verify = verify;
      this._passReqToCallback = options.passReqToCallback;
    }

所以,基本上,它会检查您是否正确地传入了回调,并检查您是否传入了选项对象。然后检查此选项对象以查看配置,它通过与 || 短路来实现。象征。因此,如果您不传入任何配置,它将默认为 usernamepassword 参数。

因此,按如下方式在您的本地配置中传递 -- 在本示例中,我将用户名换成电子邮件。

passport.use('local-signup', new LocalStrategy({
  usernameField: 'email',
  passwordField : 'password',
  passReqToCallback : true
}, 
function(req, email, password, done){
  if(req.session.user && req.session){
      done(null, false, {message: 'User is already logged in, signup illegal'});
  }
  else{......

最后,进入您的模型或数据库模式并确保适当地更改列以匹配。

额外信息: "Why do we need to configure PassportJS?"(回答是因为我也遇到了同样的问题)

答案是您需要根据您的数据库需求对其进行配置;您可能正在使用 MySQL、MongoDB 或其他一些数据库,并且每个 PassportJS 实现都必须以某种方式适合该数据库。以下是 PassportJS 在没有用户自定义的情况下无法自行解决的问题。

  • 我需要使用 ORM 吗?
  • 我需要显示什么样的错误信息?
  • 我还需要接受哪些其他字段?

TL;DR 按照本地护照创建者的预期更改预期参数,将选项对象传递给您的新本地策略配置。

您需要告诉 Passport 您将使用电子邮件字段作为用户名。

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

passport.deserializeUser(function(id, done) {

    db.User.findById(id).then(function(user) {
            done(null, user);
        })
        .catch(function(err){
            done(err, null);
        });
});

passport.use('local-signup', new LocalStrategy({
        // by default, local strategy uses username and password, we will override with email
        usernameField: 'email',
        passwordField: 'password',
        passReqToCallback: true
    },
    function(req, email, password, done){
        if(req.session.user && req.session){
            done(null, false, {message: 'User is already logged in, signup illegal'});
        }
        else{
            db.User.findOne({ where: {email : email }})
                .then(function(user) {
                    if(user !== null) {
                        done(null, false, {message: 'User already exists'});
                    }
                    else{
                        //create user yada yada
                    }
                })
                .catch(function(err){
                    return done(null, false, {message: err});
                });
        }
    }
));

您可以通过从正文中获取字段来将用户名与电子邮件结合使用。

req.body.username;