邮箱登录:前端校验与后端Redis缓存策略 🔐
在构建Web平台时,邮箱登录功能是用户认证的重要环节。本文将介绍一种结合前端校验和后端Redis缓存的邮箱登录实现逻辑,旨在提高安全性和效率。
前言 🌐
随着互联网服务的多样化,用户登录方式也在不断演进。邮箱登录因其便捷性和普及性,成为了许多Web平台的首选认证方式。本文将探讨在实现邮箱登录过程中,如何通过前端校验和后端Redis缓存策略,提高登录流程的安全性和效率。
前端邮箱输入 🛠️
注:代码不是完整代码 因为涉及到其他模块的引用 这里只是提供一个实现思路 具体代码要根据项目去更改
<template>
<el-form size="large" class="login-content-form" :model="state.ruleForm">
<el-form-item
class="login-animation1"
prop="email"
:rules="[
{ required: true, message: '请输入邮箱地址', trigger: 'blur' },
{ type: 'email', message: '请输入正确的邮箱地址', trigger: ['blur', 'change'] } ]" >
<el-input text placeholder="请输入邮箱" v-model="state.ruleForm.email" clearable autocomplete="off">
<template #prefix>
<i class="iconfont icon-dianhua el-input__icon"></i>
</template>
</el-input>
</el-form-item>
<el-form-item class="login-animation2">
<el-col :span="15">
<el-input
text maxlength="4" placeholder="请输入验证码"
v-model="state.ruleForm.code"
@keyup.enter="onSignIn"
clearable
autocomplete="off">
<template #prefix>
<el-icon class="el-input__icon">
<ele-Position/>
</el-icon>
</template>
</el-input>
</el-col>
<el-col :span="1"></el-col>
<el-col :span="8">
<el-button v-waves class="login-content-code" @click="get_email_code" :disabled="isCountDownDisabled">{{
countDownText
}}</el-button>
</el-col>
</el-form-item>
<el-form-item class="login-animation3">
<el-button type=""
class="login-content-submit" round v-waves
style="background-color: #1e1e1e; color: #fff"
@click="onSignIn"
:loading="state.loading.signIn">
<span style="font-size: 1.2rem; font-weight: bolder">登 录</span>
</el-button>
</el-form-item>
</el-form>
</template>
<script setup name="loginEmail">
// 定义变量内容
const storesThemeConfig = useThemeConfig();
const {themeConfig} = storeToRefs(storesThemeConfig);
const route = useRoute();
const router = useRouter();
const state = reactive({
email_code: '',
ruleForm: {
email: '',
code: '',
},
loading: {
signIn: false,
},
});
// 时间获取
const currentTime = computed(() => {
return formatAxis(new Date());
});
// TODO: 验证码应该设置过期
// 获取邮箱验证码
const get_email_code = async () => {
if (state.ruleForm.email.toLowerCase() === '') {
ElMessage.error('请重新输入邮箱');
return
}
// 触发按钮倒计时
countDownFunction(120);
useUserApi().getCode({email: state.ruleForm.email})
.then(async res => {
state.email_code = res.data;
})
.catch((e) => {
console.log('错误信息: ', e)
})
};
const onSignIn = async () => {
state.loading.signIn = true;
console.log(state.ruleForm.code)
console.log(state.email_code)
if (state.ruleForm.code !== state.email_code) {
ElMessage.error('验证码错误,请重新输入');
state.ruleForm.code = ''; // 清空输入框
state.loading.signIn = false;
return
}
useUserApi().signIn({email: state.ruleForm.email})
.then(async res => {
// 存储token
Session.set('token', res.data.token);
// 存储用户信息
await useUserInfo().setUserInfos();
// 后端获取菜单数据
await initBackEndControlRoutes();
// 前端获取菜单数据
// await initFrontEndControlRoutes();
signInSuccess(false);
})
.catch((e) => {
console.log('错误信息: ', e)
state.loading.signIn = false;
})
};
// 登录成功后的跳转
const signInSuccess = (isNoPower) => {
if (isNoPower) {
ElMessage.warning('抱歉,您没有登录权限');
Session.clear();
} else {
// 初始化登录成功时间问候语
let currentTimeInfo = currentTime.value;
// 登录成功,跳到转首页
// 如果是复制粘贴的路径,非首页/登录页,那么登录成功后重定向到对应的路径中
if (route.query?.redirect) {
router.push({
path: route.query?.redirect,
query: Object.keys(route.query?.params).length > 0 ? JSON.parse(route.query?.params) : '',
});
} else {
router.push('/home');
}
// 登录成功提示
const signInText = '欢迎回来!';
ElMessage.success(`${currentTimeInfo},${signInText}`);
// 添加 loading,防止第一次进入界面时出现短暂空白
NextLoading.start();
}
state.loading.signIn = false;
};
</script>
后端邮箱验证码发送 📧
- 生成验证码:在用户请求发送验证码时,后端生成一个随机的验证码。
def get_str_code(length=4):
# 生成一个随机的UUID
random_uuid = uuid.uuid4()
# 将UUID转换为32位的十六进制字符串
code = random_uuid.hex
# 截取指定长度的字符串
return code[:length]
- 发送验证码:通过邮件服务将验证码发送到用户邮箱。
def send_email(config):
send_to = config['send_to']
content = config['content']
subject = config['subject']
send_by = config['send_by']
mail_host = config['mail_host']
port = config['port']
mail_pass = config['mail_pass']
# 构建邮件内容
message = MIMEText(content, 'plain', 'utf-8')
message['From'] = send_by # 发件人
message['To'] = send_to # 收件人
message['Subject'] = subject # 邮件主题
try:
smtpObj = smtplib.SMTP_SSL(mail_host, port) # 启用SSL发信, 端口一般是465
smtpObj.login(send_by, mail_pass) # 登录验证
smtpObj.sendmail(send_by, send_to.split(','), message.as_string()) # 发送
logger.info("邮箱验证码发送成功!")
except Exception as e:
logger.warning(e)
"""
qq 邮箱授权码位于 qq 邮箱 app 的“设置”—“账号”—“授权设置”—“第三方授权”中,
具体步骤包括:打开 qq 邮箱 app、点击“我”、进入“设置”、选择“账号”、进入“授权设置”、
查看“第三方授权”,找到您需要查看授权码的应用,点击右侧的“查看授权”按钮,即可获取授权码。
"""
""" 邮件格式
email_config = {
'send_to': "", # 收件人邮箱,可以是列表
'content': "这是邮件内容。", # 邮件内容
'subject': "", # 邮件主题
'send_by': "", # 发件人邮箱
'mail_host': "smtp.qq.com", # SMTP服务器地址
'port': 465, # SMTP服务器端口
'mail_pass': "" # 授权密码,非登录密码
}
"""
def send_emai_code(send_to: str):
email_config = config.email_config
# 生成验证码
verificate_code = get_str_code()
# 获取 send_to 邮箱地址的前3位和@符号前4位的内容
formatted_email = send_to[0:3] + '...' + send_to[send_to.index('@') - 4:send_to.index('@')]
email_config['send_to'] = send_to
# 构建格式化的邮件内容
email_config['content'] = f"""【HXAutoTest】你正在登录 {formatted_email},
验证码:{verificate_code}。
提供给他人会导致账号泄露,若非本人操作,请修改密码!"""
try:
send_email(email_config)
return verificate_code
except Exception as e:
logger.warning(f"发送验证码失败:\n{e}")
return ''
后端Redis缓存策略 💾
在邮箱登录流程中,后端需要处理验证码的生成、发送和验证。使用Redis作为缓存解决方案,可以有效地提高这一流程的效率。
验证码生成与发送
- 生成验证码:在用户请求发送验证码时,后端生成一个随机的验证码。
- 发送验证码:通过邮件服务将验证码发送到用户邮箱。
- 缓存验证码:将验证码及其有效期存储在Redis中,以键值对的形式,键为用户邮箱,值为验证码。
验证码验证
当用户输入验证码进行登录时,后端需要:
- 查询Redis:根据用户邮箱查询Redis中的缓存验证码。
- 验证验证码:比对用户输入的验证码与Redis中的缓存值。
- 更新状态:如果验证码匹配,允许用户登录;否则,提示错误信息。
结语 📘
通过前端的邮箱格式校验和后端的Redis缓存策略,我们可以构建一个既高效又安全的邮箱登录系统。这种方法不仅提升了用户体验,也增强了系统的安全性。随着技术的发展,我们应不断探索和优化用户认证流程,以适应不断变化的网络环境。