【Python】邮箱登录验证码功能实现

发布于:2025-02-11 ⋅ 阅读:(30) ⋅ 点赞:(0)

邮箱登录:前端校验与后端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缓存策略,我们可以构建一个既高效又安全的邮箱登录系统。这种方法不仅提升了用户体验,也增强了系统的安全性。随着技术的发展,我们应不断探索和优化用户认证流程,以适应不断变化的网络环境。


网站公告

今日签到

点亮在社区的每一天
去签到