SpringBoot整合邮箱验证码实现用户注册

这篇具有很好参考价值的文章主要介绍了SpringBoot整合邮箱验证码实现用户注册。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

唠嗑部分

今天我们来分享一下在系统开发过程中,如何使用验证码来验证用户并完成用户注册

首先来看一下成品界面展示

SpringBoot整合邮箱验证码实现用户注册

说一下以上注册功能的设计:

用户手动输入用户名(全数据库唯一)、密码、确认密码、邮箱地址(单个邮箱最多可注册3个用户)、正确的邮箱验证码,即可注册

先来展示一下

输入用户信息

SpringBoot整合邮箱验证码实现用户注册

收到邮箱验证码

SpringBoot整合邮箱验证码实现用户注册

注册成功

言归正传

使用验证码这种方式呢是比较常见的,我们在注册App的时候,会有手机验证码、邮箱的类似哈(发邮件是免费的),邮箱验证也能够保证其真实性,防止恶意用户非法注册

下面我们就来说一下,这一系列的实现思路

1、用户名唯一验证就省略了哈,不在此范围内

2、在用户填入信息时前端先做必传、格式验证。

3、邮箱验证通过后,点击发送验证码时,携带邮箱参数请求后端接口。

4、后端生成并发送验证码后,将验证码进行分布式存储,如存到redis,key为邮箱,value为验证码,失效时间设置3分钟。

5、用户在3分钟内收到验证码并填入注册表单,请求用户注册接口。

6、后端在收到注册请求后,首先验证参数的合法性,验证通过后,根据邮箱去redis查询验证码。

7、未查询到验证码,则说明验证码已经过期,返回验证码校验失败,查询到验证码后,与表单中的验证码进行比较,相同则继续注册逻辑,不同则返回验证码校验失败。

代码环节

1、Vue组件

<template>
  <div class="header-all">
      <!--...-->
    <el-dialog :visible.sync="loginFlag" width="500px" :close-on-click-modal="false" :show-close="true">
      <!--登录表单省略...-->
      <el-form ref="registerForm" v-show="!login" :model="registerInfo" :rules="registerRules" class="register-form"
               autocomplete="on"
               label-position="left">
        <div class="title-container">
          <h3 class="title" style="text-align: center;font-size: 20px;margin-bottom: 15px;">
            {{ sysInfo.sysTitle }}注册</h3>
        </div>

        <el-form-item prop="userName">
          <el-input
              prefix-icon="el-icon-user"
              ref="username"
              v-model="registerInfo.userName"
              placeholder="请输入用户名"
              name="userName"
              type="text"
              tabindex="1"
              autocomplete="on"
          />
        </el-form-item>

        <el-tooltip v-model="capsTooltip" content="Caps lock is On" placement="right" manual>
          <el-form-item prop="password">
            <el-input
                show-password
                prefix-icon="el-icon-lock"
                ref="password"
                v-model="registerInfo.password"
                placeholder="请输入账户密码"
                name="password"
                tabindex="2"
                autocomplete="on"
                @blur="capsTooltip = false"
            />
            <span class="show-pwd">
          </span>
          </el-form-item>
        </el-tooltip>

        <el-tooltip v-model="capsTooltip" content="Caps lock is On" placement="right" manual>
          <el-form-item prop="password2">
            <el-input
                show-password
                prefix-icon="el-icon-lock"
                ref="password2"
                v-model="registerInfo.password2"
                placeholder="请确认密码"
                name="password2"
                tabindex="2"
                autocomplete="on"
                @blur="capsTooltip = false"
            />
            <span class="show-pwd">
          </span>
          </el-form-item>
        </el-tooltip>

        <el-form-item prop="email">
          <el-input
              prefix-icon="el-icon-message"
              ref="email"
              v-model="registerInfo.email"
              placeholder="请输入邮箱地址,每个邮箱最多可绑定3个账号"
              name="email"
              type="text"
              tabindex="1"
              autocomplete="on"
          />
        </el-form-item>

        <el-form-item prop="code">
          <el-row :gutter="20">
            <el-col :span="12">
              <el-input
                  ref="code"
                  type="text"
                  prefix-icon="el-icon-key"
                  v-model="registerInfo.code"
                  placeholder="请输入邮箱验证码"
                  name="code"
                  tabindex="2"
                  autocomplete="on"
                  @keyup.enter.native="registerHandle"
              />
            </el-col>
            <el-col :span="6" :offset="3">
              <el-button type="primary" size="small" :disabled="pause" @click="sendEmailCode">获取验证码 <span v-if="pause">{{ time }}</span>
              </el-button>
            </el-col>
          </el-row>
        </el-form-item>

        <el-button :loading="loading" type="primary" style="width:100%;margin-bottom:30px;"
                   @click.native.prevent="registerHandle">注册
        </el-button>

        <div style="text-align: right">
          <el-link type="primary" :underline="false" @click.native="nativeToLogin">已有账号,去登录>></el-link>
        </div>
      </el-form>
    </el-dialog>

    <el-dialog
        title="新用户提示"
        :visible.sync="dialogVisible"
        width="30%">
      <span>系统检测到您是新用户,请及时更新信息</span>
      <span slot="footer" class="dialog-footer">
        <el-button @click="dialogVisible = false">取消</el-button>
        <el-button type="primary" @click="toUpdateSelfInfo">去更新</el-button>
      </span>
    </el-dialog>
  </div>
</template>

<script>
<!--引入...-->
const defaultRegisterInfo = {
  userName: '',
  password: '',
  password2: '',
  email: '',
  code: ''
}
export default {
  <!--...-->
  data() {
    var checkUserName = (rule, value, callback) => {
      const pattern = /^[A-Za-z0-9-_]+$/;
      if (!value) {
        return callback(new Error('登录名不能为空'));
      }
      if (pattern.test(value) && value.length <= 16) {
        checkUserNameExist({userName: value}).then(res => {
          if (res.data) {
            callback(new Error('登录名已存在'));
          } else {
            callback();
          }
        })
      } else {
        callback(new Error('登录名须由数字、英文字母、-、下划线(不包含、)组成,不大于16位'));
      }
    };
    var validatePass = (rule, value, callback) => {
      if (value === '') {
        callback(new Error('请输入密码'));
      } else {
        if (this.registerInfo.password !== '') {
          this.$refs.registerForm.validateField('password2');
        }
        callback();
      }
    };
    var validatePass2 = (rule, value, callback) => {
      if (value === '') {
        callback(new Error('请再次输入密码'));
      } else if (value !== this.registerInfo.password) {
        callback(new Error('两次输入密码不一致!'));
      } else {
        callback();
      }
    };
    var validateEmail = (rule, value, callback) => {
      const pattern = /\w[-\w.+]*@([A-Za-z0-9][-A-Za-z0-9]+\.)+[A-Za-z]{2,14}/;
      if (value && value !== '') {
        if (pattern.test(value)) {
          checkEmailBindStatus({email: value}).then(res => {
            if (res.data) {
              callback();
            } else {
              callback(new Error('当前邮箱绑定用户已达到上限'));
            }
          })
        } else {
          callback(new Error('邮箱格式不正确'));
        }
      } else {
        callback(new Error('邮箱为必传项'));
      }
    };
    return {
      defaultUserImg: defaultUserImg,
      refresh: true,
      baseUrl: URL_PREFIX,
      codeUrl: URL_PREFIX + '/auth/getCaptcha',
      rules: {
        username: [{required: true, trigger: 'blur', message: '用户名为必填项'}],
        password: [{required: true, trigger: 'blur', message: '密码为必填项'}],
        code: [{required: true, trigger: 'blur', message: '验证码为必填项'}]
      },
      loading: false,
      registerInfo: {
        userName: '',
        password: '',
        password2: '',
        email: '',
        code: ''
      },
      time: 30,
      pause: false,
      registerRules: {
        userName: [{validator: checkUserName, trigger: 'blur'}],
        password: [{validator: validatePass, trigger: 'blur'}],
        password2: [{validator: validatePass2, trigger: 'blur'}],
        email: [{validator: validateEmail, trigger: 'blur'}],
        code: [{required: true, trigger: 'blur', message: '验证码为必填项'}]
      },
      dialogVisible: false
    }
  },
  methods: {
    // 用户注册
    registerHandle() {
      this.$refs.registerForm.validate(valid => {
        if (valid) {
          this.loading = true
          const registerInfo = {
              ...this.registerInfo,
              password: this.$encruption(this.registerInfo.password), 
              password2: this.$encruption(this.registerInfo.password2)
          }
          register(registerInfo).then(res => {
            this.$message.success('用户注册成功')
            this.loading = false
            this.registerInfo = defaultRegisterInfo
            this.$refs.registerForm.resetFields()
            this.nativeToLogin()
          })
        } else {
          console.log('error submit!!')
          return false
        }
        setTimeout(() => {
          this.loading = false
        }, 2000)
      })
    },
    // 发送验证码
    sendEmailCode() {
      this.$refs.registerForm.validateField('email', valid => {
        if (!valid) {
          if (this.pause) {
            this.$message.error('操作太频繁,请稍后再试')
          } else {
            sendEmailCode({email: this.registerInfo.email}).then(res => {
              this.$message.success('验证码发送成功,请注意查收!')
              this.pause = true
              const timer = setInterval(() => {
                if (this.time > 0) {
                  this.time = this.time - 1;
                }
                if (this.time <= 0) {
                  this.pause = false;
                  clearInterval(timer)
                  this.time = 30
                }
              }, 1000)
            })
          }
        }
      });
    }
  }
}
</script>

<style scoped lang="less">
   <!--css省略-->
</style>

2、api

// 发送验证码
export function sendEmailCode(data = {}) {
  return request({
    url: URL_PREFIX + '/main/user/sendEmailCode',
    method: 'post',
    data
  })
}
// 注册
export function register(data = {}) {
  return request({
    url: URL_PREFIX + '/main/user/register',
    method: 'post',
    data
  })
}

3、服务端发送验证码

@PostMapping("/user/sendEmailCode")
@ApiOperation("发送验证码处理器")
public BaseResult sendEmailCode(@RequestBody @Validated SendEmailCodeDTO dto, HttpServletRequest request){
    BaseResult result = BaseResult.ok();
    baseService.sendEmailCode(dto, request, result);
    return result;
}

实现类

@Override
public void sendEmailCode(SendEmailCodeDTO dto, HttpServletRequest request, BaseResult result) {
    long startTime = System.currentTimeMillis();
    try {
        // 验证码发送频率控制验证
        String string = redisUtil.getString(redisUtil.getCacheKey(CachePrefixContent.PUBLIC_EMAIL_CODE_TIME, dto.getEmail()));
        if (StringUtils.hasLength(string)) {
            result.setCode(CurrencyErrorEnum.OPERA_ERROR.getCode());
            result.setMsg(CurrencyErrorEnum.OPERA_ERROR.getMsg() + ",验证码发送过于频繁,请稍后再试");
        } else {
            // 生成6位数验证码
            String code = StringUtil.generatorCode(6);
            // 将验证码存入redis,有效期3分钟
            redisUtil.set(redisUtil.getCacheKey(CachePrefixContent.PUBLIC_EMAIL_CODE, dto.getEmail()), code, 3L, TimeUnit.MINUTES);
            // 调用mail发送邮件
            sendMailUtil.sendMail(dto.getEmail(), "邮箱验证码", sendMailUtil.buildCodeContent(code));
            // 验证码发送频率控制
            redisUtil.set(redisUtil.getCacheKey(CachePrefixContent.PUBLIC_EMAIL_CODE_TIME, dto.getEmail()), dto.getEmail(), 30L, TimeUnit.SECONDS);
        }
    } catch (Exception e) {
        log.error("邮箱验证码发送失败,{}", e);
        result.setCode(CurrencyErrorEnum.OPERA_ERROR.getCode());
        result.setMsg(CurrencyErrorEnum.OPERA_ERROR.getMsg());
    } finally {
        long endTime = System.currentTimeMillis();
        log.info("【{}】【邮箱验证码发送接口】【{}ms】 \n入参:{}\n出参:{}", "发送验证码", endTime - startTime, dto, result);
    }
}

4、服务端用户注册接口

@PostMapping("/user/register")
@ApiOperation("新用户注册处理器")
public BaseResult register(@RequestBody @Validated UserRegirsterDTO dto, HttpServletRequest request){
    BaseResult result = BaseResult.ok();
    userService.register(dto, request, result);
    return result;
}

实现类,非必要代码就省略了哈

@Override
@Transactional
public void register(UserRegirsterDTO dto, HttpServletRequest request, BaseResult result) {
    long startTime = System.currentTimeMillis();
    try {
        // 因为传输过程中对密码进行加密了,先解密在验证长度
        String password = RSAUtil.decrypt(dto.getPassword(), commonConfig.getRsaPrivateKey());
        if (password.length() > 16) {
            result.setCode(CurrencyErrorEnum.REQUEST_PARAMETER_ERROR.getCode());
            result.setMsg(CurrencyErrorEnum.REQUEST_PARAMETER_ERROR.getMsg() + ",用户密码最大16个字符");
            return;
        }
        //  去redis获取邮箱验证码
        String sysCode = redisUtil.getString(redisUtil.getCacheKey(CachePrefixContent.PUBLIC_EMAIL_CODE, dto.getEmail()));
        // 验证码存在且与参数传递过来的相等
        if (StringUtils.hasLength(sysCode) && sysCode.equals(dto.getCode())) {
            // 清楚这个验证码
            redisUtil.removeKey(redisUtil.getCacheKey(CachePrefixContent.PUBLIC_EMAIL_CODE, dto.getEmail()));
            // 邮箱绑定账号数验证 ...
            // 用户名唯一验证 ...
            // 用户注册
            User user = new User();
            BeanUtils.copyProperties(dto, user);
            user.setCreateTime(LocalDateTime.now());
            user.setUserId(IdUtil.simpleUUID());
            user.setPassword(passwordEncoder.encode(password));
            int insert = userMapper.insert(user);
            // 用户注册后的一系列权限分配,初始化...
        } else {
            // 未查询到验证码,返回验证码校验失败
            result.setCode(CurrencyErrorEnum.OPERA_ERROR.getCode());
            result.setMsg(CurrencyErrorEnum.OPERA_ERROR.getMsg() + ",验证码校验失败");
        }
    } catch (Exception e) {
        log.error("新用户注册失败,{}", e);
        result.setCode(CurrencyErrorEnum.OPERA_ERROR.getCode());
        result.setMsg(CurrencyErrorEnum.OPERA_ERROR.getMsg());
        TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
    } finally {
        long endTime = System.currentTimeMillis();
        log.info("【{}】【新用户注册接口】【{}ms】 \n入参:{}\n出参:{}", "新增", endTime - startTime, JSON.toJSONString(dto), result);
    }
}
结语

今天这个案例就分享到这,总结一下

1、在发送请求时,作为前端需根据需求严格的对参数进行较验,无误后发送请求。

2、作为后端来讲,接口设计应极为严格,需自行参数验证,不能相信前端,因为很有可能,设计的接口会脱离浏览器被访问。

3、此案例代码较多,部分不相关的代码省略了,请周知。

4、制作不易,一键三连再走吧,您的支持永远是我最大的动力!文章来源地址https://www.toymoban.com/news/detail-474462.html

到了这里,关于SpringBoot整合邮箱验证码实现用户注册的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 尚融宝20-实现用户注册和用户认证

    目录 一、需求 二、前端整合发送验证码 三、实现用户注册 1、创建VO对象 2、定义常量 3、引入MD5工具类 4、Controller 5、Service 6、前端整合 四、实现用户登录 1、后端整合JWT 2、前端整合 五、校验用户登录 1、后端 2、前端   点击获取验证码后先对手机号进行验证是否为空,其

    2023年04月21日
    浏览(54)
  • HTML实现用户注册页面

    效果图: 编写代码的时候用到了大量的正则表达式,大家可以去主页里找一下,里面有一篇就是关于常用正则表达式的介绍。

    2024年02月11日
    浏览(38)
  • Java开发:实现用户注册登录的功能

    一、前言 在Java开发过程中,实现用户的注册功能是最基本的,用户通过手机号或者邮箱作为注册账号也是非常常见的操作方式,不管是通过手机号注册或者邮箱注册,原理都差不多,那么本文就来分享一下在Java开发过程中的用户注册账号的功能实现。 二、准备工作 1、通过

    2023年04月09日
    浏览(50)
  • Django用户认证: 利用Django Auth模块实现用户注册、登录与登出

    用户登录注册属于用户认证的一部分,Django内置了一套用户认证体系,使用起来比较方便,而且支持用户定制和拓展,足以满足任何复杂的业务需求。 Django框架中,用户权限管理被划分为三个层次: 用户 :系统使用者,拥有自己的权限。可被一个或多个用户组包含。 用户组

    2023年04月20日
    浏览(58)
  • 用前端框架Bootstrap和Django实现用户注册页面

    命令如下: 执行下面条命令依次创建需要的应用: 名叫users的应用创建好后,还需要在全局配置文件中对应在用进行注册,具体方法如下: 打开““E:Python_projectP_001myshop-testmall_backendmall_backendsettings.py””文件,找到名叫“INSTALLED_APPS”的列表(list),在其中加入应用名,

    2024年02月06日
    浏览(54)
  • MVC框架实现用户登录注册功能(连接数据库)

    一、简单理解MVC框架 二、项目结构 三、项目源码 3.1 User 3.2 UserDao 3.3 RegisterDao 3.4 servletControll 3.5 servletControllRegister 3.6 web.xml 3.7 login.jsp 3.8 register.jsp 3.9 success.jsp 3.10 failure.jsp  四、实现效果 总结 本篇文章主要介绍利用MVC框架去实现一个简单的用户登录注册功能,内容主

    2024年02月06日
    浏览(61)
  • 使用Django Rest Framework设计与实现用户注册API

    在现代Web应用开发中,RESTful API已成为前后端分离架构中的关键组件。Django Rest Framework (DRF) 是一款基于Django的优秀库,提供了丰富的工具和接口,极大地简化了RESTful API的设计与实现。本文将以用户注册功能为例,展示如何运用DRF构建一个完整的API端点,包括数据验证、模型

    2024年04月25日
    浏览(35)
  • 用Java代码实现学生管理系统(可实现用户登录注册功能)

    简单实现学生系统的登录和注册,以及学生信息添加、删除,修改、查询功能。根据需求,创建一个学生类和和用户类以及学生系统类,在登录管理系统之前需要先注册用户,只有输入正确的用户名和密码才可以登录,忘记密码后可以根据用户信息进行修改,用容器存储学生

    2024年02月05日
    浏览(47)
  • “Layui用户认证:实现安全高效的登录和注册体验”

    layui(谐音:类 UI) 是一套开源的 Web UI 解决方案,采用自身经典的模块化规范,并遵循原生 HTML/CSS/JS 的开发方式,极易上手,拿来即用。其风格简约轻盈,而组件优雅丰盈,从源代码到使用方法的每一处细节都经过精心雕琢,非常适合网页界面的快速开发。layui 区别于那些基

    2024年02月16日
    浏览(46)
  • Django实现用户注册登录,表单提交后跳转网页(学习笔记)

    效果图如下:   使用命令提示符,进入想存放项目的目录: 在项目coco目录下新建static文件夹,用于存放网页文件的css,js,imgs 在coco_app目录下新建文件夹templates,用于存放需要用到的HTML网页 打开coco文件夹中的settings.py注册coco_app 引用templates 连接数据库,这里我用的MySQL数据

    2023年04月11日
    浏览(82)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包