javascript 护照:允许使用姓名和电子邮件地址进行注册吗?(本地策略)

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/32398120/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-28 15:12:30  来源:igfitidea点击:

Passport: Allow sign up with name and email address? (Local Strategy)

javascriptnode.jspassport.jspassport-local

提问by Forivin

Is there any way to allow a user to register on the local strategy with his password, email and name?
Every example I could find online only use name/password or email/password.

有没有办法允许用户使用他的密码、电子邮件和姓名在本地策略上注册?
我可以在网上找到的每个示例都只使用名称/密码或电子邮件/密码。

I also searched through the the whole passport documentation, but that documentation isn't helpful at all. It's just one bloated site full of examples.
I just need an list of functions, classes and variables passport uses with explanations what they and every parameter of them do. Every good library has something like that, why can't I find it for passport?

我还搜索了整个护照文件,但这些文件根本没有帮助。这只是一个充满示例的臃肿网站。
我只需要一个护照使用的函数、类和变量的列表,并解释它们和它们的每个参数的作用。每个好的图书馆都有这样的东西,为什么我找不到护照?

Here are the key parts of my code:

以下是我的代码的关键部分:

passport.use('local-signup', new LocalStrategy({
    usernameField: 'email',
    passwordField: 'password',
    //are there other options?
    //emailField did not seem to do anything
    passReqToCallback: true // allows us to pass in the req from our route (lets us check if a user is logged in or not)
},
function(req, email, password, done) {
    //check if email not already in database
        //create new user using "email" and "password"
        //I want an additional parameter here "name"
}));

So is passport really that limited? There has to be a way to do this, right?

那么护照真的那么有限吗?必须有办法做到这一点,对吧?

回答by Eugene Obrezkov

You can be a little confused but passport doesn't implement signup methods. It's just authorisation library. So you must handle that use-case on your own.

您可能会有点困惑,但 Passport 没有实现注册方法。这只是授权库。因此,您必须自己处理该用例。

First of all, create route that will be responsible for sign-up and your checks:

首先,创建将负责注册和检查的路由:

signup: function (req, res) {
  User
    .findOne({
      or: [{username: req.param('username')}, {email: req.param('email')}]
    })
    .then(function(user) {
      if (user) return {message: 'User already exists'};          
      return User.create(req.allParams());
    })
    .then(res.ok)
    .catch(res.negotiate);
}

The example above is based on Sails framework, but you can fit it with no problems to your own case.

上面的例子是基于 Sails 框架的,但你可以毫无问题地将它适配到你自己的案例中。

Next step is include passport local strategy.

下一步是包括护照本地策略。

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

var LOCAL_STRATEGY_CONFIG = {
  usernameField: 'email',
  passwordField: 'password',
  session: false,
  passReqToCallback: true
};

function _onLocalStrategyAuth(req, email, password, next) {
  User
    .findOne(or: [{email: email}, {username: email}])
    .then(function (user) {
      if (!user) return next(null, null, {
        code: 'E_USER_NOT_FOUND',
        message: email + ' is not found',
        status: 401
      });

      if (!HashService.bcrypt.compareSync(password, user.password)) return next(null, null, {
        code: 'E_WRONG_PASSWORD',
        message: 'Password is wrong',
        status: 401
      });

      return next(null, user, {});
    })
    .catch(next);
}

passport.use(new LocalStrategy(LOCAL_STRATEGY_CONFIG), _onLocalStrategyAuth));

We have only signin task now. It's simple.

我们现在只有登录任务。这很简单。

signin: function(req, res) {
  passport.authenticate('local', function(error, user, info) {
    if (error || !user) return res.negotiate(Object.assign(error, info));
    return res.ok(user);
  })(req, res);
}

This way is more suitable for passport and works great for me.

这种方式更适合护照,对我很有用。

回答by Meir

Here is what worked for me, the solution is based on a mongoose based odm, the first part is the passport related part, I also attached the user part from odm to who how the encryption of the password is done.

这是对我有用的方法,该解决方案基于基于猫鼬的 odm,第一部分是与护照相关的部分,我还将 odm 中的用户部分附加到密码加密是如何完成的。

If I understood your question, you want the user to type either his email or password. In this case modify the search to try both, that is, match the provided user identifier (in your call to findOne(...) with either the username or password.

如果我理解您的问题,您希望用户输入他的电子邮件或密码。在这种情况下,修改搜索以尝试两者,即将提供的用户标识符(在您对 findOne(...) 的调用中)与用户名或密码匹配。

Note that I use bcrypt to avoid storing clear passwords, that's why there is a customized compare method for testing passwords. Also note 'hints' of using google auth as well, My system enabled both, if it is relevant, please lemme know and I can add the required code.

请注意,我使用 bcrypt 来避免存储明文密码,这就是为什么有一个用于测试密码的自定义比较方法。还要注意使用 google auth 的“提示”,我的系统同时启用了两者,如果相关,请让我知道,我可以添加所需的代码。

------------ Auth part (just relevant snippets) -----------

------------ Auth 部分(只是相关的片段) -----------

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

passport.serializeUser(function(user, done) {
    // the values returned here will be used to deserializeUser
    // this can be use for further logins
    done(null, {username: user.username, _id: user.id, role: user.role});
});

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


passport.use(new LocalStrategy(function(username, password, done){
    odm.User.findOne({username: username, authType: 'direct'}, function(err, user){
        if(err){
            return done(err, false);
        }
        if(!user){
            return done(null, false);
        }
        if(user.role === 'new'){
            console.log('can not use new user!');
            return done('user not activated yet, please contact admin', false);
        }
        user.comparePassword(password,function(err, isMatch){
            if(err){
                return done(err, false);
            }
            if(isMatch){
                return done(null, user);//{username: username});
            }
            return done(null, false);
        });
    });
}));
app.post('/login',  function(req, res, next){
        passport.authenticate('local', {
            failureRedirect: '/logout?status=login failed'
        }, function(err, user, info){
                if(err){
                    return next(err);
                }
                if(!user){
                    return res.redirect('/login');
                }
                req.logIn(user, function(err){
                    if (req.body.rememberme) {
                        req.session.cookie.maxAge = 30*24*60*60*1000 ;//Rememeber 'me' for 30 days
                    } else {
                        req.session.cookie.expires = false;
                    }
                    var redirect = req.param('redirect') || '/index';
                    res.redirect(redirect);
                });
            }
        )(req, res, next);
    }
);

app.post('/register',function(req, res){
    var user = new odm.User({username: req.body.username, password: req.body.password, email: req.body.email, authType: 'direct'});
    user.save(function(err, user){
        if(err){
            console.log('registration err: ' , err);
        } else {
            res.redirect('/list');
        }
    });
});

--- user/odm, relevant parts ----------------

--- 用户/odm,相关部分 ----------------

var bcrypt = require('bcrypt-nodejs');

// --------------------- User ------------------------------------------ //
var userSchema = new Schema({
    name: String,
    email: String,
    username: {type: String, required: true, unique: true},
    password: String,
    role: {type: String, required: true, enum: ['new', 'admin', 'user'], default: 'new'},
    authType: {type: String, enum: ['google', 'direct'], required: true}
});

userSchema.pre('save', function (next) {
    var user = this;
    if (!user.isModified('password')) return next();

    console.log('making hash...........');
    bcrypt.genSalt(SALT_WORK_FACTOR, function (err, salt) {
        if (err) return next(err);

        bcrypt.hash(user.password, salt, null, function (err, hash) {
            if (err) return next(err);
            user.password = hash;
            next();
        });
    });
});

userSchema.methods.comparePassword = function (candidatePassword, cb) {
    bcrypt.compare(candidatePassword, this.password, function (err, isMatch) {
        if (err) return cb(err);
        cb(null, isMatch);
    });
};

回答by Mohammad Amir

var localStrategy = require('passport-local').Strategy;
var User = require('../public/models/user');

module.exports = function(passport){

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

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

    passport.use('local-signup', new localStrategy({
        usernameField: 'email',
        passwordField: 'password',
        passReqToCallback: true
    },
    function(req, email, password, done){
        process.nextTick(function(){
             User.findOne({'local.enroll': email}, function(err, user){
                 if(err)
                     return done(err);
                 if(user){
                     return done(null, false, req.flash('signupmessage', 'The email already taken'));
                 } else{
                     var newUser = new User();
                     newUser.local.enroll = email;
                     newUser.local.password = newUser.generateHash(password);                     
                     newUser.save(function(err){
                         if(err)
                             throw err
                         return done(null, newUser);
                     });
                 }

             });
        });
    }));

    passport.use('local-login', new localStrategy({
        usernameField: 'email',
        passwordField: 'password',
        passReqToCallback: true
    },
    function(req, email, password, done){
        process.nextTick(function(){
             User.findOne({'local.enroll': email}, function(err, user){
                 if(err)
                     return done(err);
                 if(!user){
                     return done(null, false, req.flash('loginmessage', 'No user found'));
                 }
                 if(!user.validPassword(password)){
                     return done(null, false, req.flash('loginmessage', 'Invalid password'));
                 }
                 return done(null, user);
             });
        });
    }));    
}

回答by Fattie

Say you have this

说你有这个

app.post('/login', urlencodedParser,
    // so, user has been to /loginpage and clicked submit.
    // /loginpage has a post form that goes to "/login".
    // hence you arrive here.
    passport.authenticate('my-simple-login-strategy', {
        failureRedirect: '/loginagain'
    }),
        function(req, res) {
            console.log("you are in ............")
            res.redirect('/stuff');
    });

Note that the .authenticatehas an explicit tag.

请注意,.authenticate具有显式标记。

The tags is 'my-simple-login-strategy'

标签是 'my-simple-login-strategy'

That means you have this ...

这意味着你有这个......

passport.use(
    'my-simple-login-strategy',
    // !!!!!!!!!!!!!note!!!!!!!!!!, the DEFAULT there (if you have nothing)
    // is 'local'. A good example of defaults being silly :/
    new Strategy(
        STRAT_CONFIG,
        function(email, password, cb) {
           // must return cb(null, false) or cb(null, the_user_struct) or cb(err)
           db.findUserByEmailPass(email, password, function(err, userFoundByDB) {
                if (err) { return cb(err); }
                if (!userFoundByDB) { return cb(null, false); }
                console.log('... ' + JSON.stringify(userFoundByDB) )
                return cb(null, userFoundByDB)
           })
        }
    )
)

!!! !!! NOTE THAT 'local' IS JUST THE DEFAULT TAG NAME !!! !!!

!!!!!!请注意,“本地”只是默认标签名称!!!!!!

In passport.use, we always put in an explicit tag. It is much clearer if you do so. Put in an explicit tag in the strategy and in the app.postwhen you use the strategy.

在 中passport.use,我们总是放入一个明确的标签。如果你这样做就更清楚了。在策略中和app.post何时使用策略中放入显式标签。

So that's my-simple-login-strategy.

这就是我的简单登录策略。

What is the actual db.findUserByEmailPass sql function?

实际的 db.findUserByEmailPass sql 函数是什么?

We'll come back to that!

我们会回到那个!

So we have my-simple-login-strategy

所以我们有我的简单登录策略

Next ...... we need my-simple-createaccount-strategy

接下来......我们需要 my-simple-createaccount-strategy

Note that we are still sneakily using passport.authenticate:

请注意,我们仍在偷偷使用passport.authenticate:

So:

所以:

the strategy my-simple-createaccount-strategy will actually make an account.

my-simple-createaccount-strategy 策略实际上会创建一个帐户。

However .............

然而 .............

you should still return a struct.

你仍然应该返回一个结构。

Note that my-simple-login-strategy has to return a struct.

请注意, my-simple-login-strategy 必须返回一个结构体。

So, my-simple-createaccount-strategy also has to return a struct - in exactly the same way.

因此, my-simple-createaccount-strategy 也必须以完全相同的方式返回一个结构体。

app.post('/createaccount', urlencodedParser,
    // so, user has been to /createanaccountform and clicked submit,
    // that sends a post to /createaccount. So we are here:
    passport.authenticate('my-simple-createaccount-strategy', {
        failureRedirect: '/loginagain'
    }),
        function(req, res) {
            console.log("you are in ............")
            res.redirect('/stuff');
    });

And here's the strategy ..........

这是策略…………

passport.use(
    'my-simple-createaccount-strategy',
    new Strategy(
        STRAT_CONFIG,
        function(email, password, cb) {
            // return cb(null, false), or cb(null, the_user_struct) or cb(err)
            db.simpleCreate(email, password, function(err, trueOrFalse) {
                if (err) { return cb(err); }
                if (!trueOrFalse) { return cb(null, false); }
                return cb(null, trueOrFalse)
            })
        }
    )
)

The strategy is pretty much the same. But the db call is different.

策略几乎相同。但是 db 调用是不同的。

So now let's look at the db calls.

所以现在让我们看看 db 调用。

Let's look at the db calls!

让我们看看 db 调用!

The ordinary db call for the ordinary strategy is going to look like this:

普通策略的普通 db 调用将如下所示:

exports.findUserByEmailPass = function(email, password, cb) {
    // return the struct or false via the callback
    dc.query(
        'select * from users where email = ? and password = ?',
        [email, password],
        (error, users, fields) => {
            if (error) { throw error } // or something like cb(new Error('blah'));
            cb(null, (users.length == 1) ? users[0] : false)
        })
}

So that's exports.findUserByEmailPass, which is used by my-simple-login-strategy.

这就是 export.findUserByEmailPass,它由 my-simple-login-strategy 使用。

But what about exports.simpleCreate for my-simple-createaccount-strategy?

但是对于 my-simple-createaccount-strategy 来说,exports.simpleCreate 呢?

A simple toy version would

一个简单的玩具版本

  1. check if the username exists already - return false at this point if it does exist already, then
  2. create it, and then
  3. actually just return the record again.
  1. 检查用户名是否已经存在 - 如果它已经存在,此时返回 false,然后
  2. 创建它,然后
  3. 实际上只是再次返回记录。

Recall that (3) is just like in the ordinary "find" call.

回想一下,(3) 就像在普通的“查找”调用中一样。

Remember ... the strategy my-simple-createaccount-strategy will actually makean account. But you should still returna struct in the same wayas your ordinary authenticate strategy, my-simple-login-strategy.

请记住...... my-simple-createaccount-strategy 策略实际上会创建一个帐户。但是您仍然应该与您的普通身份验证策略 my-simple-login-strategy相同的方式返回一个结构体。

So exports.simpleCreate is a simple chain of three calls:

所以exports.simpleCreate 是一个简单的三个调用链:

exports.simpleCreate = function(email, password, cb) {
    // check if exists; insert; re-select and return it
    dc.query(
        'select * from users where email = ?', [email],
        (error, users, fields) => {
            if (error) { throw error } // or something like cb(new Error('blah'));
            if (users.length > 0) {
                return cb(null, false)
            }  
            else {
                return partTwo(email, password, cb)
            }
        })
}

partTwo = function(email, password, cb) {
    dc.query(
        'insert into users (email, password) values (?, ?)', [email, password],
        (error, users, fields) => {
            if (error) { throw error } // or something like cb(new Error('blah'));
            partThree(email, password, cb)
        })
}

partThree = function(email, password, cb) {
    dc.query(
        'select * from users where email = ? and password = ?', [email, password],
        (error, users, fields) => {
            if (error) { throw error } // or something like cb(new Error('blah'));
            cb(null, (users.length == 1) ? users[0] : false)
        })
}

And that all works.

这一切都有效。

But note that

但请注意

passport has nothing to dowith account creation!

护照与创建帐户无关

In fact, you do not have to use a strategy at all.

事实上,您根本不必使用策略。

In app.post('/createaccount'you can, if you wish, do nothing with passport.authenticate... don't even mention it in the code. Don't use authenticate at all. Just go ahead and do the sql process of inserting a new user, right there in app.post.

app.post('/createaccount'你可以,如果你愿意,什么都不passport.authenticate做......甚至不要在代码中提到它。根本不要使用身份验证。继续执行插入新用户的 sql 过程,就在 app.post 中。

However, if you "trickily" use a passport strategy - my-simple-createaccount-strategy in the example - you have the bonus that the user is then immediately logged-in with a session and everything works in the same pattern as the login post. Cool.

但是,如果您“巧妙地”使用通行证策略 - 在示例中为 my-simple-createaccount-strategy - 您将获得奖励,然后用户立即通过会话登录,并且一切都以与登录帖子相同的模式运行. 凉爽的。

回答by Kurt Van den Branden

This has actually nothing to do with passportand is pretty simple, assuming you are using body-parser. Make sure you have an input fieldin your form with the attribute name="name"where you register the user's name like:

这实际上已经没有任何关系passport,是非常简单的,假设你使用body-parser。确保您input field的表单中有一个具有name="name"注册用户名的属性,例如:

<div class="form-group">
    <label for="signup-name">Name</label>
    <input type="text" placeholder="Name" name="name">
</div> 

In your routing, you can access this field with req.body.name:

在您的路由中,您可以使用以下命令访问此字段req.body.name

passport.use('local-signup', new LocalStrategy({
    usernameField: 'email',
    passwordField: 'password',
    //are there other options?
    //emailField did not seem to do anything
    passReqToCallback: true
},
function(req, email, password, done) {
    //check if email not already in database
    //create new user using "email" and "password"
    //I want an additional parameter here "name"

    user.email = email;
    user.password = password; // Do some hashing before storing

    user.name = req.body.name;

}));

So you can add as many form input fields as you want, access them by the value of the name attribute. A Second example would be:

因此,您可以根据需要添加任意数量的表单输入字段,并通过 name 属性的值访问它们。第二个例子是:

<input type="text" placeholder="City" name="city">
<input type="text" placeholder="Country" name="country">

// Access them by
user.city = req.body.city;
user.country = req.body.country;

回答by Yusufbek

UserModel.find({email: req.body.email}, function(err, user){                                               
    if(err){                                                                  
       res.redirect('/your sign up page');                                                                          
    } else {                                                                  
      if(user.length > 0){                                                    
       res.redirect('/again your sign up page');                                                                      
      } else{                                                               
        //YOUR REGISTRATION CODES HERE                                                                     
       }                                                                          
    }                                                                        
})