如何允许用户使用node.js通过用户名或电子邮件登录

时间:2018-01-06 09:48:08

标签: javascript node.js authentication passport.js

我有一个带护照的node.js登录系统,但我试图弄清楚如何用用户名或电子邮件登录用户。我只能单独使用电子邮件或用户名登录用户。我不知道如何编写代码来满足用户名和电子邮件的需求。因此,如果用户想要使用用户名登录,他们可以或者如果希望使用他们的电子邮件,他们也可以。这是我的users.js文件中的localstrategy代码:

passport.use(new LocalStrategy(
    function(email, password, done) {
        User.getUserByEmail(email, function(err, user, next){
            if(err) throw err;
            if(!user){
                return done(null, false, {message: 'Unknown user'});
            }

        User.comparePassword(password, user.password, function(err, isMatch){
            if(err) throw err;
            if(isMatch){
                return done(null, user);
            } else {
                return done(null, false, {message: 'Invalid password'});
            }
        });
        });
    }));

这是我的module.exports在我的user.js:

module.exports.getUserByEmail = function(email, callback){
    var query = {email: email};
    User.findOne(query, callback);
}

module.exports.getUserById = function(id, callback){
    User.findById(id, callback);
}

module.exports.comparePassword = function(candidatePassword, hash, callback){
    bcrypt.compare(candidatePassword, hash, function(err, isMatch) {
        if(err) throw err;
        callback(null, isMatch);
    });
}

以上代码仅允许用户使用他们的电子邮件登录。我希望用户有机会使用他们的电子邮件或用户名登录。

4 个答案:

答案 0 :(得分:0)

您可以在getUserById的回调中使用getUserByEmail,以便同时针对emailusername运行这两个查询。如果您指定email,则emailOrUserName将具有email值,然后User.getUserByEmail将返回用户,然后它将继续comparePassword

如果您有username,则emailOrUserName将具有username值,然后User.getUserByEmail将不会返回用户,因此会执行User.getUserById并且在那里找到用户然后它将继续User.comparePassword否则它将返回Unknown user

passport.use(new LocalStrategy(
    function(emailOrUserName, password, done) {
        User.getUserByEmail(emailOrUserName, function(err, user, next){
            if(err) throw err;
            if(!user){
              User.getUserById(emailOrUserName, function(err, user, next){
                   if(err) throw err;
                   if(!user){
                      return done(null, false, {message: 'Unknown user'});
                   }
            }

        User.comparePassword(password, user.password, function(err, isMatch){
            if(err) throw err;
            if(isMatch){
                return done(null, user);
            } else {
                return done(null, false, {message: 'Invalid password'});
            }
        });
        });
    }));

答案 1 :(得分:0)

在身份验证中间件中期望 usernamepassword,然后继续使用找到的任何值作为找到用户的条件。

中间件示例:

function authenticateUser(req, res, done) {

  let username = req.body.username,
      password = req.body.password,
      email    = req.body.email;
  let conditions = !!username ? {username: username} : {email: email};

  UserModel.findOne(conditions, (err, user) => {
    if (err)   return done(err);
    if (!user) return done(new Error('Incorrect username or email'));

    return user.comparePassword(password, user.password)
    .then(match => {
      if (match) return done();
      else       return done(new Error('Incorrect password'));
    })
    .catch(error => {
      if (error) return done(new Error(`Unable to validated password. - ${error}`));
    });
  });

}

现在,拥有正确文档的前端开发人员现在可以实际使用usernameemail两者(您将需要一些JavaScript)两者)在使用端点构建登录表单时。这是同时使用两者的示例:

HTML:

<form id="login-form" method="POST" action="/login">
  <input id="username-or-email" type="text" placeholder="Username or Email" required/>
  <input type="password" name="password" placeholder="Password"  required/>
  <input type="submit"/>
</form>

JavaScript:

// select the form element
let loginForm = document.querySelector('#login-form');

// add a form handler on submit
loginForm.addEventListener("submit", formHandler);

// validate and the set name attribute as appropriate
function formHandler() {
  /** W3C Email regex: (RFC5322) */
  const email_regex = /^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/;
  /** Must starts with a letter then can include underscores (_) & hyphens (-) */
  const username_regex = /^[a-zA-Z][\w-]+$/;

  let input = document.querySelector('#username-or-email');

  if (email_regex.test(input.value)) {
    // it is an email, send the value as an email
    input.setAttribute("name", "email");
  } else if (username_regex.test(input.value)) {
    // it is a username, send the value as a username
    input.setAttribute("name", "username");
  } else {
    // invalid email or username format, return an error message to user
  }

}

因此,您可以同时验证和设置动态输入。请记住,正则表达式应尽可能匹配您的用户名和电子邮件数据模型。

答案 2 :(得分:0)

按照当地策略进行

function(username, password, done) {
    var criteria = (username.indexOf('@') === -1) ? {username: username} : {email: username};
    User.findOne(criteria, function (err, user) { //implementation }

答案 3 :(得分:0)

刚刚进入这种情况,我虽然会分享我的最终代码以使用用户名电子邮件来验证用户:

userSchema.statics.findByCredentials = async credentials => {
    const {
        email = undefined,
        username = undefined,
        password = undefined
    } = credentials

    if ((!!email && !!username) || (!email && !username)) {
        throw new Error('Should provide either email or username.')
    }

    if (!password) {
        throw new Error('Password is required.')
    }

    const user = await User.findOne(email ? { email } : { username })
    if (!user) {
        throw new Error('Credentials are invalid!')
    }

    if (!bcrypt.compare(password, user.password)) {
        throw new Error('Credentials are invalid!')
    }

    return user
}

所以我使用这个函数来验证用户是否提供了有效的凭据,并从我的处理程序中调用 model 类上的函数。