600字范文,内容丰富有趣,生活中的好帮手!
600字范文 > nodejs+express实现用户登录或者注册通过邮箱发送验证码(redis验证)

nodejs+express实现用户登录或者注册通过邮箱发送验证码(redis验证)

时间:2020-03-07 04:03:09

相关推荐

nodejs+express实现用户登录或者注册通过邮箱发送验证码(redis验证)

❤️砥砺前行,不负余光,永远在路上❤️

❤️砥砺前行,不负余光,永远在路上❤️

简要目录

实现思路一、后端部分(文件目录可以看图2)1.redis部分2.nodemailer部分3.发送邮件的接口4.后端校验验证码是否有效 二、前端部分(使用的element-admin)1.正则验证输入的是否是邮箱号2.前端login页面完整代码可以参考(有部分字段需要修改),这个包括60秒倒计时的效果。 总结

实现思路

有帮助的话各位哥哥可以点个关注收藏哦

后端生成六位随机验证码,存入redis(key:邮箱号,value:验证码),校验的接口/code/login通过redis 查询code是否存在,如果满足条件,可以自己加一些登录注册的业务之后在返回需要的值。

有帮助的话各位哥哥可以点个关注收藏哦

一、后端部分(文件目录可以看图2)

1.redis部分

代码如下(示例):

const redis = require('redis');const client = redis.createClient(); //默认没有密码 127.0.0.1 端口也是默认// 如果是连接远程的话// redis[s]://[[username][:password]@][host][:port][/db-number]:// const client = createClient({// url: 'redis://alice:foobared@awesome.redis.server:6380'// });client.on('error', (err) =>console.log('Redis Client Error', err));client.on('connect', () => {console.log('redis connect success');})client.connect();module.exports = client;

2.nodemailer部分

代码如下(示例):

//nodemailer.jsconst nodemailer = require('nodemailer');const {mailConfig } = require('../config/index')const {user, pass } = mailConfiglet transporter = nodemailer.createTransport({//node_modules/nodemailer/lib/well-known/services.json 查看相关的配置,如果使用qq邮箱,就查看qq邮箱的相关配置service: 'qq', //类型qq邮箱port: 465,secure: true, // true for 465, false for other portsauth: {user,pass}});//pass 不是邮箱账户的密码而是stmp的授权码(必须是相应邮箱的stmp授权码)//邮箱---设置--账户--POP3/SMTP服务---开启---获取stmp授权码module.exports = function (email, code) {//const {username,password,email} = userlet mailOptions = {from: '<nmxgzs@>', // 发送方to: email, //接收者邮箱,多个邮箱用逗号间隔subject: `欢迎登录,你的验证码${code}`, // 标题html: `<head><base target="_blank" /><style type="text/css">::-webkit-scrollbar{display: none; }</style><style id="cloudAttachStyle" type="text/css">#divNeteaseBigAttach, #divNeteaseBigAttach_bak{display:none;}</style><style id="blockquoteStyle" type="text/css">blockquote{display:none;}</style><style type="text/css">body{font-size:14px;font-family:arial,verdana,sans-serif;line-height:1.666;padding:0;margin:0;overflow:auto;white-space:normal;word-wrap:break-word;min-height:100px} td, input, button, select, body{font-family:Helvetica, \'Microsoft Yahei\', verdana} pre {white-space:pre-wrap;white-space:-moz-pre-wrap;white-space:-pre-wrap;white-space:-o-pre-wrap;word-wrap:break-word;width:95%} th,td{font-family:arial,verdana,sans-serif;line-height:1.666} img{border:0} header,footer,section,aside,article,nav,hgroup,figure,figcaption{display:block} blockquote{margin-right:0px}</style></head><body tabindex="0" role="listitem"><table width="700" border="0" align="center" cellspacing="0" style="width:700px;"><tbody><tr><td><div style="width:700px;margin:0 auto;border-bottom:1px solid #ccc;margin-bottom:30px;"><table border="0" cellpadding="0" cellspacing="0" width="700" height="39" style="font:12px Tahoma, Arial, 宋体;"><tbody><tr><td width="210"></td></tr></tbody></table></div><div style="width:680px;padding:0 10px;margin:0 auto;"><div style="line-height:1.5;font-size:14px;margin-bottom:25px;color:#4d4d4d;"><strong style="display:block;margin-bottom:15px;">尊敬的用户:<span style="color:#f60;font-size: 16px;"></span>您好!</strong><strong style="display:block;margin-bottom:15px;">您正在进行<span style="color: red">用户登录</span>操作,请在验证码输入框中输入:<span style="color:#f60;font-size: 24px">${code}</span>,以完成操作。</strong></div><div style="margin-bottom:30px;"><small style="display:block;margin-bottom:20px;font-size:12px;"><p style="color:#747474;">注意:此操作可能会修改您的密码、登录邮箱或绑定手机。如非本人操作,请及时登录并修改密码以保证帐户安全<br>(工作人员不会向你索取此验证码,请勿泄漏!)</p></small></div></div><div style="width:700px;margin:0 auto;"><div style="padding:10px 10px 0;border-top:1px solid #ccc;color:#747474;margin-bottom:20px;line-height:1.3em;font-size:12px;"><p>此为系统邮件,请勿回复<br>请保管好您的邮箱,避免账号被他人盗用</p><p>网络科技团队</p></div></div></td></tr></tbody></table></body>`};transporter.sendMail(mailOptions, (error, info) => {if (error) {return console.log(error);}console.log('mail sent:', info.response);});};

3.发送邮件的接口

router中引入redis 和 nodemailer 部分

const client = require('../utils/redis');//redis使用const nodemailer = require('../utils/nodemailer');//发送邮件

//成功返回参数function success (res, total = null) {if (total) {return {code: 200,data: res,msg: '成功',total}} else {return {code: 200,data: res,msg: '成功'}}}//失败参数function fail (msg) {return {code: 500,msg}}// 生成六位随机验证码function createCode () {return parseInt(Math.random() * 1000000)// return 'xxxxxx'.replace(/[xy]/g, function (c) {// var r = (Math.random() * 16) | 0// var v = c == 'x' ? r : (r & 0x3) | 0x8// return v.toString(16)// })}//发送验证码邮件router.post('/send/email', function (req, response, next) {let code = createCode() //随机生成验证码const mail = req.body.mail//请求携带的邮件client.set(mail, code).then(res => {//存入redis//设置成功发送邮件nodemailer(mail, code)response.send(success())})client.expire(mail, 60);//设置过期时间 60s 前端六十秒可以重新获取});

4.后端校验验证码是否有效

//通过验证码登录router.post('/code/login', function (req, response, next) {/* 这里 用户名就是 邮件 密码就是code */const {mail, code} = req.bodyclient.get(mail).then(res => {//从redis查询数据if (code== res) {console.log('验证成功')//do something// ...response.send(success({user: mail,}))} else {console.log('验证失败')response.send(fail('验证失败'))}})});

二、前端部分(使用的element-admin)

1.正则验证输入的是否是邮箱号

const validateUsername = (rule, value, callback) => {let reg = /^[A-Za-z\d]+([-_.][A-Za-z\d]+)*@([A-Za-z\d]+[-.])+[A-Za-z\d]{2,5}$/;// !reg.test(value)if (!reg.test(value)) {callback(new Error('请输入正确邮箱号码'))} else {callback()}}

2.前端login页面完整代码可以参考(有部分字段需要修改),这个包括60秒倒计时的效果。

<template><div class="login-container"><el-form ref="loginForm" :model="loginForm" :rules="loginRules" class="login-form" autocomplete="on"label-position="left"><div class="title-container"><h3 class="title">{{$t('login.title') }}</h3><!-- <lang-select class="set-language" /> --></div><el-form-item prop="username"><el-row style="padding-right:5px"><el-col :span="18"><span class="svg-container"><svg-icon icon-class="user" /></span><el-input ref="username" v-model="loginForm.username" placeholder="请输入邮箱" name="username" type="text"tabindex="1" autocomplete="on" /></el-col><el-col :span="6" style="margin-top:7px"><el-button type="primary" :disabled="disable" :class="{ codeGeting:isGeting }" @click="getVerCode">{{getCode}}</el-button></el-col></el-row></el-form-item><el-tooltip v-model="capsTooltip" content="Caps lock is On" placement="right" manual><el-form-item><span class="svg-container"><svg-icon icon-class="password" /></span><el-input :key="passwordType" ref="password" v-model="loginForm.password" placeholder="请输入六位验证码"name="password" tabindex="2" autocomplete="on" @keyup.native="checkCapslock" @blur="capsTooltip = false"@keyup.enter.native="handleLogin" /><!-- <span class="show-pwd" @click="showPwd"><svg-icon :icon-class="passwordType === 'password' ? 'eye' : 'eye-open'" /></span> --></el-form-item></el-tooltip><el-button :loading="loading" type="primary" style="width:100%;margin-bottom:30px;"@click.native.prevent="handleLogin">{{$t('login.logIn') }}</el-button><div style="position:relative"><div class="other-login"><div class="title">推荐使用其他方式登录</div><img src="@/assets/mp.png" class="wx-logo" title="小程序登录" alt="小程序登录" @click="otherLogin"></div></div></el-form><el-dialog title="微信扫码登录" :visible.sync="showDialog" align="center" width="30%" @close="wxLoginClose"><div><el-image :src="qrUrl" alt="小程序码" height="10%" /><div style="margin:15px 0">请使用微信扫描小程序码登录{{bindTimeout ? '(已超时)' : '' }}</div><!-- (后期考虑是否启用选择性授权) --><!-- <div>启用授权获取用户信息:<el-switch v-model="auth" active-color="#13ce66" inactive-color="#ff4949" @change="authChange" /></div> --></div></el-dialog></div></template><script>import {validUsername } from '@/utils/validate'import {getCode, getToken, getUUid, sendMail, codeLogin } from '@/api/user'import {GlobalGetUuidShort } from '@/utils/index'export default {name: 'Login',components: {},data () {const validateUsername = (rule, value, callback) => {let reg = /^[A-Za-z\d]+([-_.][A-Za-z\d]+)*@([A-Za-z\d]+[-.])+[A-Za-z\d]{2,5}$/;// !reg.test(value)if (!reg.test(value)) {callback(new Error('请输入正确邮箱号码'))} else {callback()}}const validatePassword = (rule, value, callback) => {if (value.length < 6) {callback(new Error('密码不能少于6位'))} else {callback()}}return {qrUrl: '',auth: true,bindTimeout: false,timer: null, // 定时器loginForm: {username: '',password: ''},loginRules: {username: [{required: true, trigger: 'blur', validator: validateUsername }],},passwordType: 'password',capsTooltip: false,loading: false,showDialog: false,redirect: undefined,otherQuery: {},getCode: '获取验证码',isGeting: false,count: 60,disable: false}},watch: {$route: {handler: function (route) {const query = route.queryif (query) {this.redirect = query.redirectthis.otherQuery = this.getOtherQuery(query)}},immediate: true}},created () {// window.addEventListener('storage', this.afterQRScan)},mounted () {if (this.loginForm.username === '') {this.$refs.username.focus()} else if (this.loginForm.password === '') {this.$refs.password.focus()}},destroyed () {// window.removeEventListener('storage', this.afterQRScan)},methods: {//获取验证码getVerCode () {if (this.loginForm.username) {sendMail(this.loginForm).then(res => {console.log(res, 'res')})var countDown = setInterval(() => {if (this.count < 1) {this.isGeting = falsethis.disable = falsethis.getCode = '获取验证码'this.count = 60clearInterval(countDown)} else {this.isGeting = truethis.disable = truethis.getCode = this.count-- + '秒后重发'}}, 1000)} else {this.$notify.error('请必须输入邮箱号码')}},//关闭弹窗清除定时器wxLoginClose () {this.timer && clearTimeout(this.timer)this.bindTimeout = false},// 点击其他方式登录otherLogin () {getToken().then(r => {this.showDialog = truethis.getQrUrl()})},changeQr () {if (this.bindTimeout) {this.bindTimeout = falsethis.getQrUrl()} else {this.$notify.warning('请当前二维码过期之后重新获取')}},getQrUrl () {let uuid = GlobalGetUuidShort(), counter = 1this.qrUrl = `/api/getCode?useAuth=1&uuid=${uuid}`this.timer && clearTimeout(this.timer)// 清除定时器重新开启this.timer = setInterval(() => {getUUid({uuid }).then((res) => {// 获取openidcounter++if (counter === 31) {//超时clearTimeout(this.timer)this.bindTimeout = true}if (res.data.openid !== '') {clearTimeout(this.timer)this.showDialog = falsethis.$store.dispatch('user/login', res.data).then(() => {// 登录跳转 (扫码登录)this.$router.push({path: this.redirect || '/dashboard', query: this.otherQuery })}).catch(err => {console.log(err, 'err')})}}).catch((err) => {clearTimeout(this.timer)})}, 2000)},// 修改选项重新获取qr// authChange (val) {// console.log(val)// this.$nextTick(function () {//this.qrUrl = `/api/getCode?uuid=${this.uuid}` + '&useAuth=' + (val ? 1 : 0)// })// },checkCapslock (e) {const {key } = ethis.capsTooltip = key && key.length === 1 && (key >= 'A' && key <= 'Z')},showPwd () {if (this.passwordType === 'password') {this.passwordType = ''} else {this.passwordType = 'password'}this.$nextTick(() => {this.$refs.password.focus()})},handleLogin () {this.$refs.loginForm.validate(valid => {if (valid) {this.loading = true// this.$message.warning('开发中,目前仅支持扫码登录')codeLogin(this.loginForm).then(res => {console.log(res, 'res')this.loading = falsethis.$store.dispatch('user/login', res.data).then(() => {console.log(55, '55')this.$router.push({path: this.redirect || '/dashboard', query: this.otherQuery })}).catch(() => {// this.loading = false})//this.$router.push({ path: this.redirect || '/dashboard', query: this.otherQuery })})// this.loading = true// this.$store.dispatch('user/login', this.loginForm)// .then(() => {//this.$router.push({ path: this.redirect || '/', query: this.otherQuery })//this.loading = false// })// .catch(() => {//this.loading = false// })} else {console.log('error submit!!')return false}})},getOtherQuery (query) {return Object.keys(query).reduce((acc, cur) => {if (cur !== 'redirect') {acc[cur] = query[cur]}return acc}, {})}// afterQRScan() {// if (e.key === 'x-admin-oauth-code') {//const code = getQueryObject(e.newValue)//const codeMap = {// wechat: 'code',// tencent: 'code'//}//const type = codeMap[this.auth_type]//const codeName = code[type]//if (codeName) {// this.$store.dispatch('LoginByThirdparty', codeName).then(() => {// this.$router.push({ path: this.redirect || '/' })// })//} else {// alert('第三方登录失败')//}// }// }}}</script><style lang="scss">/* 修复input 背景不协调 和光标变色 *//* Detail see /PanJiaChen/vue-element-admin/pull/927 */$bg: #283443;$light_gray: #fff;$cursor: #fff;@supports (-webkit-mask: none) and (not (cater-color: $cursor)) {.login-container .el-input input {color: $cursor;}}/* reset element-ui css */.login-container {.el-input {display: inline-block;height: 47px;width: 85%;input {background: transparent;border: 0px;-webkit-appearance: none;border-radius: 0px;padding: 12px 5px 12px 15px;color: $light_gray;height: 47px;caret-color: $cursor;&:-webkit-autofill {box-shadow: 0 0 0px 1000px $bg inset !important;-webkit-text-fill-color: $cursor !important;}}}.el-form-item {border: 1px solid rgba(255, 255, 255, 0.1);background: rgba(0, 0, 0, 0.1);border-radius: 5px;color: #454545;}}</style><style lang="scss" scoped>$bg: #2d3a4b;$dark_gray: #889aa4;$light_gray: #eee;.codeGeting {background: #cdcdcd;border-color: #cdcdcd;}.login-container {min-height: 100%;width: 100%;background-color: $bg;overflow: hidden;.mask {opacity: 0.2;}.login-form {position: relative;width: 520px;max-width: 100%;padding: 160px 35px 0;margin: 0 auto;overflow: hidden;.other-login {margin-top: 30px;text-align: center;.title {color: #dcdfe6;position: relative;font-size: 14px;&:before {position: absolute;left: 0;top: 50%;content: '';width: 100px;height: 1px;background: #dcdfe6;display: inline-block;}&:after {position: absolute;right: 0;top: 50%;content: '';width: 100px;height: 1px;background: #dcdfe6;display: inline-block;}}.wx-logo {margin-top: 20px;width: 36px;height: 36px;border-radius: 100%;cursor: pointer;}}}.tips {font-size: 14px;color: #fff;margin-bottom: 10px;span {&:first-of-type {margin-right: 16px;}}}.svg-container {padding: 6px 5px 6px 15px;color: $dark_gray;vertical-align: middle;width: 30px;display: inline-block;}.title-container {position: relative;.title {font-size: 26px;color: $light_gray;margin: 0px auto 40px auto;text-align: center;font-weight: bold;}.set-language {color: #fff;position: absolute;top: 3px;font-size: 18px;right: 0px;cursor: pointer;}}.show-pwd {position: absolute;right: 10px;top: 7px;font-size: 16px;color: $dark_gray;cursor: pointer;user-select: none;}.thirdparty-button {position: absolute;right: 0;bottom: 6px;}@media only screen and (max-width: 470px) {.thirdparty-button {display: none;}}}</style>

总结

贴的代码应该都是完整的,如果哪里有问题的话可以留言哦

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。