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

您所在的位置:网站首页 redis验证码倒计时 nodejs+express实现用户登录或者注册通过邮箱发送验证码(redis验证)

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

2023-08-26 12:30| 来源: 网络整理| 查看: 265

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

简要目录 实现思路一、后端部分(文件目录可以看图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:[email protected]: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.js const nodemailer = require('nodemailer'); const { mailConfig } = require('../config/index') const { user, pass } = mailConfig let 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 ports auth: { user, pass } }); //pass 不是邮箱账户的密码而是stmp的授权码(必须是相应邮箱的stmp授权码) //邮箱---设置--账户--POP3/SMTP服务---开启---获取stmp授权码 module.exports = function (email, code) { // const {username,password,email} = user let mailOptions = { from: '', // 发送方 to: email, //接收者邮箱,多个邮箱用逗号间隔 subject: `欢迎登录,你的验证码${code}`, // 标题 html: `::-webkit-scrollbar{ display: none; }#divNeteaseBigAttach, #divNeteaseBigAttach_bak{display:none;}blockquote{display:none;} 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}尊敬的用户:您好!您正在进行用户登录操作,请在验证码输入框中输入:${code},以完成操作。 注意:此操作可能会修改您的密码、登录邮箱或绑定手机。如非本人操作,请及时登录并修改密码以保证帐户安全(工作人员不会向你索取此验证码,请勿泄漏!)此为系统邮件,请勿回复请保管好您的邮箱,避免账号被他人盗用网络科技团队` }; 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.body client.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秒倒计时的效果。 {{ $t('login.title') }} {{getCode}} {{ $t('login.logIn') }} 推荐使用其他方式登录 请使用微信扫描小程序码登录{{ bindTimeout ? '(已超时)' : '' }} 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 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.query if (query) { this.redirect = query.redirect this.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 this.isGeting = true this.disable = true this.getCode = this.count-- + '秒后重发' } }, 1000) } else { this.$notify.error('请必须输入邮箱号码') } }, //关闭弹窗清除定时器 wxLoginClose () { this.timer && clearTimeout(this.timer) this.bindTimeout = false }, // 点击其他方式登录 otherLogin () { getToken().then(r => { this.showDialog = true this.getQrUrl() }) }, changeQr () { if (this.bindTimeout) { this.bindTimeout = false this.getQrUrl() } else { this.$notify.warning('请当前二维码过期之后重新获取') } }, getQrUrl () { let uuid = GlobalGetUuidShort(), counter = 1 this.qrUrl = `/api/getCode?useAuth=1&uuid=${uuid}` this.timer && clearTimeout(this.timer)// 清除定时器重新开启 this.timer = setInterval(() => { getUUid({ uuid }).then((res) => {// 获取openid counter++ if (counter === 31) { //超时 clearTimeout(this.timer) this.bindTimeout = true } if (res.data.openid !== '') { clearTimeout(this.timer) this.showDialog = false this.$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 } = e this.capsTooltip = key && key.length === 1 && (key >= 'A' && key 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 = false this.$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('第三方登录失败') // } // } // } } } /* 修复input 背景不协调 和光标变色 */ /* Detail see https://github.com/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; } } $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; } } } 总结

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



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3