如何用express/node确认电子邮件地址?

sin*_*Gob 31 node.js express

我试图为用户建立确认电子邮件地址,以验证他们的电子邮件是真实的.我应该用什么软件包来确认用户的电子邮件地址.到目前为止我使用猫鼬和表达

代码示例

var UserSchema = new mongoose.Schema({
    email: { type: String, unique: true, lowercase: true }
    password: String
});

var User = mongoose.model('User', UserSchema);

app.post('/signup', function(req, res, next) {
   // Create a new User
   var user = new User();
   user.email = req.body.email;
   user.password = req.body.password;
   user.save();
});
Run Code Online (Sandbox Code Playgroud)

在app.post代码中,如何确认用户的电子邮件地址?

Sov*_*iut 130

您要查找的内容称为"帐户验证"或"电子邮件验证".有很多Node模块可以执行此操作,但原理如下:

  • 您的用户模型应具有默认active属性false
  • 当用户提交有效的注册表单时,创建一个新用户(最初active将是谁false)
  • 创建随机哈希并将其存储在数据库中,并引用用户ID
  • 将电子邮件发送到提供的电子邮件地址,并将哈希作为指向服务器上路由的链接的一部分
  • 当用户单击链接并点击您的路线时,请检查URL中传递的哈希值
  • 如果数据库中存在哈希,请获取相关用户并将其active属性设置为true
  • 从数据库中删除哈希,不再需要它

您的用户现已通过验证.

  • 谢谢你。但有一个问题,我们如何确保用于确认电子邮件的公共 URL 端点不会收到垃圾邮件?(因此导致数据库性能问题)。我想我们应该实施身份验证?这将如何运作? (6认同)
  • 您错过了一个步骤,除了 OP 不关心令牌过期,但如果安全性很重要(应该如此),那么令牌应该设置为在给定时间段(可能 6 小时)后过期 (4认同)
  • @财富 是的。您可以存储创建日期,然后在检查令牌时进行比较。如果超过创建日期+有效期,请将其删除。 (3认同)
  • @DashiellRoseBark-Huss 它允许不良行为者通过使用受害者的电子邮件地址注册然后验证它而无需访问受害者的电子邮件帐户来冒充某人,从而使电子邮件验证的整个过程无效。 (2认同)

Bla*_*mba 24

var express=require('express');
var nodemailer = require("nodemailer");
var app=express();
/*
    Here we are configuring our SMTP Server details.
    STMP is mail server which is responsible for sending and recieving email.
*/
var smtpTransport = nodemailer.createTransport("SMTP",{
    service: "Gmail",
    auth: {
        user: "Your Gmail ID",
        pass: "Gmail Password"
    }
});
var rand,mailOptions,host,link;
/*------------------SMTP Over-----------------------------*/

/*------------------Routing Started ------------------------*/

app.get('/',function(req,res){
    res.sendfile('index.html');
});
app.get('/send',function(req,res){
        rand=Math.floor((Math.random() * 100) + 54);
    host=req.get('host');
    link="http://"+req.get('host')+"/verify?id="+rand;
    mailOptions={
        to : req.query.to,
        subject : "Please confirm your Email account",
        html : "Hello,<br> Please Click on the link to verify your email.<br><a href="+link+">Click here to verify</a>" 
    }
    console.log(mailOptions);
    smtpTransport.sendMail(mailOptions, function(error, response){
     if(error){
            console.log(error);
        res.end("error");
     }else{
            console.log("Message sent: " + response.message);
        res.end("sent");
         }
});
});

app.get('/verify',function(req,res){
console.log(req.protocol+":/"+req.get('host'));
if((req.protocol+"://"+req.get('host'))==("http://"+host))
{
    console.log("Domain is matched. Information is from Authentic email");
    if(req.query.id==rand)
    {
        console.log("email is verified");
        res.end("<h1>Email "+mailOptions.to+" is been Successfully verified");
    }
    else
    {
        console.log("email is not verified");
        res.end("<h1>Bad Request</h1>");
    }
}
else
{
    res.end("<h1>Request is from unknown source");
}
});

/*--------------------Routing Over----------------------------*/

app.listen(3000,function(){
    console.log("Express Started on Port 3000");
});
Run Code Online (Sandbox Code Playgroud)

按照代码示例,您可以使用nodemailer发送链接,然后验证它.这是一个链接:https://codeforgeek.com/2014/07/node-email-verification-script/

  • 我想只要 /send 和 /verify 路由命中同一个服务器程序,这就会起作用。我的意思是,如果 /send 请求被发送到服务器程序并且由于某种原因服务器崩溃并重新启动,变量 rand 将不再可用于使用 /verify 路由验证用户。变量 rand 应该存储在数据库中。 (4认同)
  • rand 应该使用加密库创建:( (2认同)
  • 为什么这个答案有这么多人点赞?“rand”将随着每个对“/send”的请求而不断变化,因此除非一次只有一个用户验证他们的电子邮件,否则这将不起作用。 (2认同)

小智 18

第1步:

用户模型

var userSchema = new mongoose.Schema({
    email: { type: String, unique: true },
    isVerified: { type: Boolean, default: false },
    password: String,
  });
Run Code Online (Sandbox Code Playgroud)

代币模型

const tokenSchema = new mongoose.Schema({
    _userId: { type: mongoose.Schema.Types.ObjectId, required: true, ref: 'User' },
    token: { type: String, required: true },
    expireAt: { type: Date, default: Date.now, index: { expires: 86400000 } }
});
Run Code Online (Sandbox Code Playgroud)

第 2 步:登录

exports.login = function(req, res, next) {
    User.findOne({ email: req.body.email }, function(err, user) {
        // error occur
        if(err){
            return res.status(500).send({msg: err.message});
        }
        // user is not found in database i.e. user is not registered yet.
        else if (!user){
            return res.status(401).send({ msg:'The email address ' + req.body.email + ' is not associated with any account. please check and try again!'});
        }
        // comapre user's password if user is find in above step
        else if(!Bcrypt.compareSync(req.body.password, user.password)){
            return res.status(401).send({msg:'Wrong Password!'});
        }
        // check user is verified or not
        else if (!user.isVerified){
            return res.status(401).send({msg:'Your Email has not been verified. Please click on resend'});
        } 
        // user successfully logged in
        else{
            return res.status(200).send('User successfully logged in.');
        }
    });

});
Run Code Online (Sandbox Code Playgroud)

第 3 步:注册

exports.signup = function(req, res, next) {
  User.findOne({ email: req.body.email }, function (err, user) {
    // error occur
    if(err){
        return res.status(500).send({msg: err.message});
    }
    // if email is exist into database i.e. email is associated with another user.
    else if (user) {
        return res.status(400).send({msg:'This email address is already associated with another account.'});
    }
    // if user is not exist into database then save the user into database for register account
    else{
        // password hashing for save into databse
        req.body.password = Bcrypt.hashSync(req.body.password, 10);
        // create and save user
        user = new User({ name: req.body.name, email: req.body.email, password: req.body.password });
        user.save(function (err) {
            if (err) { 
              return res.status(500).send({msg:err.message});
            }
            
            // generate token and save
            var token = new Token({ _userId: user._id, token: crypto.randomBytes(16).toString('hex') });
            token.save(function (err) {
              if(err){
                return res.status(500).send({msg:err.message});
              }

                // Send email (use credintials of SendGrid)
                var transporter = nodemailer.createTransport({ service: 'Sendgrid', auth: { user: process.env.SENDGRID_USERNAME, pass: process.env.SENDGRID_PASSWORD } });
                var mailOptions = { from: 'no-reply@example.com', to: user.email, subject: 'Account Verification Link', text: 'Hello '+ req.body.name +',\n\n' + 'Please verify your account by clicking the link: \nhttp:\/\/' + req.headers.host + '\/confirmation\/' + user.email + '\/' + token.token + '\n\nThank You!\n' };
                transporter.sendMail(mailOptions, function (err) {
                    if (err) { 
                        return res.status(500).send({msg:'Technical Issue!, Please click on resend for verify your Email.'});
                     }
                    return res.status(200).send('A verification email has been sent to ' + user.email + '. It will be expire after one day. If you not get verification Email click on resend token.');
                });
            });
        });
    }
    
  });

});
Run Code Online (Sandbox Code Playgroud)

第 4 步:验证帐户

// It is GET method, you have to write like that
//    app.get('/confirmation/:email/:token',confirmEmail)

exports.confirmEmail = function (req, res, next) {
    Token.findOne({ token: req.params.token }, function (err, token) {
        // token is not found into database i.e. token may have expired 
        if (!token){
            return res.status(400).send({msg:'Your verification link may have expired. Please click on resend for verify your Email.'});
        }
        // if token is found then check valid user 
        else{
            User.findOne({ _id: token._userId, email: req.params.email }, function (err, user) {
                // not valid user
                if (!user){
                    return res.status(401).send({msg:'We were unable to find a user for this verification. Please SignUp!'});
                } 
                // user is already verified
                else if (user.isVerified){
                    return res.status(200).send('User has been already verified. Please Login');
                }
                // verify user
                else{
                    // change isVerified to true
                    user.isVerified = true;
                    user.save(function (err) {
                        // error occur
                        if(err){
                            return res.status(500).send({msg: err.message});
                        }
                        // account successfully verified
                        else{
                          return res.status(200).send('Your account has been successfully verified');
                        }
                    });
                }
            });
        }
        
    });
});
Run Code Online (Sandbox Code Playgroud)

第 5 步:重新发送链接

exports.resendLink = function (req, res, next) {

    User.findOne({ email: req.body.email }, function (err, user) {
        // user is not found into database
        if (!user){
            return res.status(400).send({msg:'We were unable to find a user with that email. Make sure your Email is correct!'});
        }
        // user has been already verified
        else if (user.isVerified){
            return res.status(200).send('This account has been already verified. Please log in.');
    
        } 
        // send verification link
        else{
            // generate token and save
            var token = new Token({ _userId: user._id, token: crypto.randomBytes(16).toString('hex') });
            token.save(function (err) {
                if (err) {
                  return res.status(500).send({msg:err.message});
                }
    
                // Send email (use credintials of SendGrid)
                    var transporter = nodemailer.createTransport({ service: 'Sendgrid', auth: { user: process.env.SENDGRID_USERNAME, pass: process.env.SENDGRID_PASSWORD } });
                    var mailOptions = { from: 'no-reply@example.com', to: user.email, subject: 'Account Verification Link', text: 'Hello '+ user.name +',\n\n' + 'Please verify your account by clicking the link: \nhttp:\/\/' + req.headers.host + '\/confirmation\/' + user.email + '\/' + token.token + '\n\nThank You!\n' };
                    transporter.sendMail(mailOptions, function (err) {
                       if (err) { 
                        return res.status(500).send({msg:'Technical Issue!, Please click on resend for verify your Email.'});
                     }
                    return res.status(200).send('A verification email has been sent to ' + user.email + '. It will be expire after one day. If you not get verification Email click on resend token.');
                });
            });
        }
    });
});
Run Code Online (Sandbox Code Playgroud)

您可以从此链接获得帮助:https : //medium.com/@slgupta022/email-verification-using-sendgrid-in-node-js-express-js-mongodb-c5803f643e09

  • **npm 安装 bcryptjs --save** (2认同)
  • **npm 安装加密 --save** (2认同)
  • **npm 安装nodemailer --save** (2认同)
  • 重新发送链接时,您是否应该检查令牌是否仍然存在并在生成新令牌之前先删除该令牌? (2认同)
  • 难道你不应该将电子邮件地址也存储在令牌对象中,然后检查电子邮件地址同时没有更改吗?否则,我可以向旧电子邮件地址请求令牌,更改帐户中的电子邮件地址,然后通过发送到旧地址的链接验证新地址。 (2认同)

Pat*_*hee 16

我想提出一种与提议的方法略有不同的方法。

此方法不会将哈希放入数据库中(因此与它的交互较少)

您不需要在数据库中注册哈希。以下是收到注册请求后的概述:

  1. 您对用户 ID + 注册时间进行编码
  2. 您将令牌发送给用户
  3. 当用户触发他的注册请求时,您对令牌进行解码。
  4. 因为解码后的令牌包含用户 id + 时间,所以您可以通过增加他们的角色(注册、订阅者、管理员等)来标记用户为已注册用户

翻译成代码,你会得到这样的东西:

1-编码令牌

function encodeRegistrationToken()
{
    // jsonweb automatically adds a key that determines the time, but you can use any module
    const jwt = require('jsonwebtoken');

    // The information we need to find our user in the database (not sensible info)
    let info = {id: yourUserId};

    // The hash we will be sending to the user
    const token = jwt.sign(info, "yoursecretkey");

    return token;
}

// ... 
let token = encodeRegistrationToken();
Run Code Online (Sandbox Code Playgroud)

2-通过任何适当的方式向用户发送令牌

// Your implementation of sending the token
sendTokenToUser(token);
Run Code Online (Sandbox Code Playgroud)

3-解码令牌

function decodeRegistrationToken(token)
{   
    const jwt = require('jsonwebtoken');
    let decoded = jwt.verify(token, "yoursecretkey");

    let userId = decoded.id;

    // Check that the user didn't take too long
    let dateNow = new Date();
    let tokenTime = decoded.iat * 1000;

    // Two hours
    let hours = 2;
    let tokenLife = hours * 60 * 1000;

    // User took too long to enter the code
    if (tokenTime + tokenLife < dateNow.getTime())
    {
        return {            
            expired: true
        };
    }

    // User registered in time
    return {
        userID
    };

}
Run Code Online (Sandbox Code Playgroud)

4 - 更新您的数据库

  • 将用户角色升级为订阅者

或者

  • 将他们的“注册”键设置为 true

快速说明:如果需要,您可以在编码令牌时进一步编码用户 ID(它很容易访问)。


Sar*_*hal 12

我花了很多时间找出发送确认邮件的完美方式。这是我使用的方法。

图书馆

const jwt = require('jsonwebtoken');
const nodemailer = require("nodemailer");
Run Code Online (Sandbox Code Playgroud)

步骤 1 将用户 ID 编码到具有到期日期的 jwt 令牌中

var date = new Date();
var mail = {
            "id": user.id,
            "created": date.toString()
            }

const token_mail_verification = jwt.sign(mail, config.jwt_secret_mail, { expiresIn: '1d' });

var url = config.baseUrl + "verify?id=" + token_mail_verification;
Run Code Online (Sandbox Code Playgroud)

步骤 2 使用 nodemailer 库将令牌发送到用户电子邮件地址

 let transporter = nodemailer.createTransport({
        name: "www.domain.com",
        host: "smtp.domain.com",
        port: 323,
        secure: false, // use SSL
        auth: {
            user: "user@domain.com", // username for your mail server
            pass: "Password", // password
        },

    });

    // send mail with defined transport object
    let info = await transporter.sendMail({
        from: '"NAME" <user@domain.com>', // sender address
        to: user.email, // list of receivers seperated by comma
        subject: "Account Verification", // Subject line
        text: "Click on the link below to veriy your account " + url, // plain text body
    }, (error, info) => {

        if (error) {
            console.log(error)
            return;
        }
        console.log('Message sent successfully!');
        console.log(info);
        transporter.close();
    });
Run Code Online (Sandbox Code Playgroud)

步骤 3 接受验证链接

app.get('/verify', function(req, res) {
    token = req.query.id;
    if (token) {
        try {
            jwt.verify(token, config.jwt_secret_mail, (e, decoded) => {
                if (e) {
                    console.log(e)
                    return res.sendStatus(403)
                } else {
                    id = decoded.id;

                
//Update your database here with whatever the verification flag you are using 



                }

            });
        } catch (err) {

            console.log(err)
            return res.sendStatus(403)
        }
    } else {
        return res.sendStatus(403)

    }

})
Run Code Online (Sandbox Code Playgroud)

第 4 步 喝杯咖啡,感谢我为您节省了这么多时间

PS:这个 nodemailer SMTP 方法甚至可以与您的主机一起使用。所以没必要找第三方。您还可以找到通过 nodemailer 使用 gmail 的方法。


小智 6

如果您只是在本地计算机上进行测试,那么一种了解如何进行测试的简单方法可以是:

假设您已经知道通过nodemailer发送邮件。

用户注册后,将注册数据存储在数据库中后,在服务器端,将接收到的注册数据中的用户电子邮件以及随机生成的数字作为用户电子邮件,并使用页面地址构建自定义网址/ she单击邮件中给定的链接。

var customUrl = "http://"+ your host + "/" + your verification web-page + "?email=" + userEmail + "&id=" + randomNumber;
Run Code Online (Sandbox Code Playgroud)

一个示例可以是:

var userEmail = someone@example.com
var host = localhost:8080
var directWebPage = verifyUserEmail.html
var randomNumber = // generate with math.random() // lets say 111
Run Code Online (Sandbox Code Playgroud)

放入上述customUrl格式中,它看起来像这样

customUrl:http://localhost:8080/verifyUserEmail.htmlemail=someone@example.com&id=111
Run Code Online (Sandbox Code Playgroud)

将此customUrl保存在某处(可能在您的数据库中)现在,使用包含此cutomUrl链接的电子邮件正文向用户发送电子邮件。

<a href="customUrl">Click to verify your email</a>
Run Code Online (Sandbox Code Playgroud)

当用户单击链接时,他/她将被定向到verifyUserEmail.html页面,当发生这种情况时,您可以提取包含电子邮件ID信息的页面URL。

例如,在有角度的地方,我会像这样

var urlVerifyData = $location.url(); or $location.absUrl();
Run Code Online (Sandbox Code Playgroud)

现在使用JavaScript字符串方法提取电子邮件表单urlVerifyData字符串

使用此电子邮件urlVerifyData请求您的服务器

现在,查询数据库的这封电子邮件,并确认之前存储customUrl与用户的urlVerifyData

如果他们匹配,呵呵!您拥有了一个真正的用户!