完成邮件发送验证码部分
1.开启SMTP服务(以qq邮箱为例)
(1).设置—>账号—>开启服务,如下图所示
(2).点击管理服务—>安全设置最下面—>生成授权码(代码中有效)
2.后端代码部分(后端使用的是express)
(1).先安装nodemailer(我使用的是6.7.0版本)
npm i nodemailer@6.7.0
(2).schma.js
// 邮箱的验证规则
const email = joi.string().email().required().error(new Error("请输入正确规则的邮箱账号"))
//发送邮箱的验证规则对象
exports.reg_sendcode_schema = {
body: {
email,
},
}
(3).router.js
//2.导入需要的验证规则对象
const {reg_sendcode_schema} = require('../schema/user')
//发送邮箱
router.post('/email',expressJoi(reg_sendcode_schema),user_handler.email)
(4).router_handler.js
//发送验证码
exports.email = (req, res) => {
const email = req.body.email;
const checkEmailSql = 'SELECT * FROM users WHERE email = ?';
db.query(checkEmailSql, [email], (err, results) => {
if (err) {
return res.cc(err);
}
if (results.length > 0) {
return res.cc('邮箱已被注册!');
}
// 每次发送前清除之前保存的验证码(用户可以多次点击发送验证码)
const clearCodeSql = "UPDATE users SET code = NULL WHERE email = '你的邮箱' ";
db.query(clearCodeSql, (err, results) => {
if (err) return res.cc(err);
let transporter = nodemailer.createTransport({
service: 'qq', //邮箱类型
secure: true, //是否使用安全连接,对https协议的
port: 465, //qq邮件服务所占用的端口
auth: {
user: "你的邮箱", //开启SMTP的邮箱,发件人
pass: "你生成的授权码" //qq授权码
},
});
//产生随机验证码
var code = "";
for (var i = 0; i < 6; i++) {
code += Math.floor(Math.random() * 10);
}
let options = {
from: "你的邮箱", //发送方
to: req.body.email, //接收方
subject: "激活验证码", //邮件主题
text:"你的验证码:" + code // 邮件正文
};
transporter.sendMail(options, (err, info) => {
if (err) {
res.send(err);
} else {
//加密验证码
code = bcrypt.hashSync(code, 10);
const sendCodeSql = "update users set code=? where email='你的邮箱'";
db.query(sendCodeSql, [code], (err, results) => {
if (err) return res.cc(err);
if (results.affectedRows !== 1) return res.cc("发送验证码失败!");
res.send({
status: 0,
message: "发送验证码成功!",
});
});
}
});
})
})
}
3.前端代码(前端我使用的是Vue3+element-plus)
(1).邮箱的接口(我使用的是axios发送请求)
//引入axios的实例
import axios from '@/http/index.js'
//邮箱的接口
export const sendcode = data => {
const {
email,
} = data
return instance({
url: '/api/email',
method: 'POST',
data: {
email,
}
})
}
(2).注册页面(这里的ElMessage是element-plus内置的弹窗提示)
//html部分
<el-button class="send-code" @click="sendCode">发送验证码</el-button>
import { ElMessage } from 'element-plus'
//js部分
//注册表单数据(:formData是ts定义的接口,做类型判断)
const registerData: formData = reactive({
username: '',
email: '',
password: '',
repassword: '',
code: null
})
const validateEmail = (value) => {
let reg = /^[A-Za-z\d]+([-_.][A-Za-z\d]+)*@([A-Za-z\d]+[-.])+[A-Za-z\d]{2,5}$/;
return reg.test(value)
}
//发送邮箱
const sendCode = async () => {
const email = registerData.email;
const res = await sendcode(registerData);
if (validateEmail(email)) {
//发送验证码成功
if (res.data.status == 0) {
//后端返回成功的值
ElMessage.success(res.data.message);
} else {
//后端返回失败的值
ElMessage.error(res.data.message);
}
} else {
ElMessage.error('请输入正确的邮箱地址');
}
}
完成注册,验证表单信息
1.验证用户全部的注册信息(下图为我的前端页面)
2.后端代码
(1).schma.js
// 用户名的验证规则
const username = joi.string().alphanum().min(3).max(10).required().error(new Error('用户名为3-10位任意字符'))
// 密码的验证规则
const password = joi.string().pattern(/^[\S]{6,12}$/).required().error(new Error("密码为6-12位任意字符"))
// 邮箱的验证规则
const email = joi.string().email().required().error(new Error("请输入正确规则的邮箱账号"))
//验证码的验证规则
const code = joi.string().regex(/^\d{6}$/).required().error(new Error("请输入六位数字的验证码"));
// 注册表单的验证规则对象
exports.reg_register_schema = {
// 表示需要对 req.body 中的数据进行验证
body: {
username,
password,
email,
code,
},
}
(2).router.js
//导入需要的验证规则对象
const {reg_register_schema} = require('../schema/user')
//注册新用户
router.post('/register',expressJoi(reg_register_schema),user_handler.register)
(3).router_handler.js
//注册新用户的处理函数
exports.register = (req, res) => {
//获取客户端提交到服务器的用户信息
const userInfo = req.body
//定义SQL语句,查询用户名是否被占用
const sqlStr = 'select * from users where username=? OR email=?'
db.query(sqlStr, [userInfo.username, userInfo.email], (err, results) => {
//执行SQL语句失败
if (err) {
return res.cc(err)
}
//判断用户名和邮箱是否被占用
if (results.length > 0) {
for (let i = 0; i < results.length; i++) {
if (results[i].username === userInfo.username) {
return res.cc('用户名已被占用,请更换其他用户名!');
}
if (results[i].email === userInfo.email) {
return res.cc('邮箱已被注册!');
}
}
}
//验证验证码
const checkcodeSql = "select code from users where email='你的邮箱'";
db.query(checkcodeSql, (err, results) => {
if (err) {
return res.cc(err);
}
if (results.length === 0) {
return res.cc("用户查找失败!");
}
const code = results[0].code;
if (!code) {
return res.cc("未发送验证码!");
}
const compareResult = bcrypt.compareSync(req.body.code, results[0].code);
if (!compareResult) {
return res.cc("验证码错误!");
} else {
// 若验证码正确之后,需要将数据库中的验证码清除
const clearcodedSql = "update users set code=null where email='你的邮箱'"
db.query(clearcodedSql, (err, results) => {
console.log(results)
if (err) {
return res.cc(err);
}
if (results.affectedRows !== 1) {
return res.cc("验证码清除失败!");
}
//调用bcrypt.hashSync()对密码进行加密
userInfo.password = bcrypt.hashSync(userInfo.password, 10)
//定义插入新用户的sql语句
const sql = 'insert into users set ?'
//注册身份
const identity = '用户'
//创建时间
const create_time = new Date()
db.query(sql, {
username: userInfo.username,
password: userInfo.password,
email: userInfo.email,
//身份
identity,
//创建时间
create_time,
//初始没冻结
status: 0,
},
function (err, results) {
// 执行 SQL 语句失败
// if (err) return res.send({ status: 1, message: err.message })
if (err) {
return res.cc(err)
}
// SQL 语句执行成功,但影响行数不为 1
if (results.affectedRows !== 1) {
// return res.send({ status: 1, message: '注册用户失败,请稍后再试!' })
return res.cc('注册用户失败,请稍后再试!')
}
// // 注册用户成功
return res.cc('注册成功!', 0)
})
})
}
})
})
}
3.前端代码
(1).用户注册的接口
//注册的接口
export const register = data => {
const {
username,
password,
email,
code,
} = data
return instance({
url: '/api/register',
method: 'POST',
data: {
username,
password,
email,
code
}
})
}
(2).前端页面
//html部分
<el-button type="primary" plain @click="Register">注册</el-button>
//js部分
//注册表单数据(:fromData是ts的定义的接口)
const registerData: formData = reactive({
username: '',
email: '',
password: '',
repassword: '',
code: null
})
//注册
const Register = async () => {
const res = await register(registerData)
if (registerData.password == registerData.repassword) {
if (res.data.status == 0) {
ElMessage({
message: res.data.message,
type: 'success'
})
//切换到登录
activeName.value = 'first'
// 清空registerData对象数据
for (let key in registerData) {
registerData[key] = null;
}
} else {
ElMessage.error(res.data.message)
}
} else {
ElMessage.error('两次密码输入不正确')
}
}
总结:
后端保存code的值是拿管理员的定义了一个code字段保存的,然后做清空添加的操作,虽然我做了很多判断处理,但感觉这个方法还是不行,可能存在bug,如果有更好的办法,把这个code缓存起来会更好。