yumuing博客:Java Golang springboot 后端微服务技术控

一文教你学会实现以邮件激活的注册账户代码

通常,我们在进行各大平台进行注册账户时,都会在邮箱收到一封激活邮件,而在点击其中的激活链接之后,我们就能够激活账户,否则,我们将无法正常使用账户,这使得服务平台所拥有的激活用户的邮件信息真实性有了保证。如果为了服务平台的长久运行,这种激活方式必不可少。

本文资源

完整可运行代码文件
静态资源为:community\src\main\resources\templates
包括:index.html、register.html、operate-result.html、activation.html
资源如下:

[hide]下载:https://wwrs.lanzouo.com/iNl3X0prwk0f 密码:b870[/hide]

实现思路

通常,我们在进行各大平台进行注册账户时,都会在邮箱收到一封激活邮件,而在点击其中的激活链接之后,我们就能够激活账户,否则,我们将无法正常使用账户,这使得服务平台所拥有的激活用户的邮件信息真实性有了保证。如果为了服务平台的长久运行,这种激活方式必不可少。为了让大家更为理解这种激活方式,博主绘制了一份关于该种激活方式的时序图,可能存在一部分错漏,也希望能够收到大家的指正!以邮件激活的注册方式相关时序图如下:


在这份时序图中,我们可以看出,这种激活方式主要分为三大阶段,其相关的步骤信息如下:

这便是实现以邮件激活的注册方式的基本实现思路,相信大家基本了解该如何去实现这样一个注册方式,接下来,博主将在 SpringBoot、Thymeleaf 以及 Mybatis 的运行环境下实现这种注册方式。

SpringBoot 实现

编写用户数据:用户包括用户 ID 、密码,加盐值、邮箱、激活状态、用户类型(用户身份:0-普通用户; 1-超级管理员; 2-版主)、用户激活状态、激活码、头像地址、创建时间。建表语句如下:

DROP TABLE IF EXISTS `user`;
/*!40101 SET @saved_cs_client     = @@character_set_client */;
 SET character_set_client = utf8mb4 ;
CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(50) DEFAULT NULL,
  `password` varchar(50) DEFAULT NULL,
  `salt` varchar(50) DEFAULT NULL,
  `email` varchar(100) DEFAULT NULL,
  `type` int(11) DEFAULT NULL COMMENT '0-普通用户; 1-超级管理员; 2-版主;',
  `status` int(11) DEFAULT NULL COMMENT '0-未激活; 1-已激活;',
  `activation_code` varchar(100) DEFAULT NULL,
  `header_url` varchar(200) DEFAULT NULL,
  `create_time` timestamp NULL DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `index_username` (`username`(20)),
  KEY `index_email` (`email`(20))
) ENGINE=InnoDB AUTO_INCREMENT=101 DEFAULT CHARSET=utf8;

利用 Mybatis 生成 user.java、mapper.xml、mapper.java、userService.java、userServiceImpl.java 文件,并实现以下查询:

<sql id="Base_Column_List">
        id,username,password,
        salt,email,type,
        status,activation_code,header_url,
        create_time
</sql>
<select id="selectOneById" resultType="User">
    select
    <include refid="Base_Column_List"/>
    from user
    where id = #{id}
</select>
<select id="selectOneByUsername" resultMap="BaseResultMap">
    select
    <include refid="Base_Column_List"/>
    from user
    where
    username = #{username,jdbcType=VARCHAR}
</select>
<select id="selectOneByEmail" resultMap="BaseResultMap">
    select
    <include refid="Base_Column_List"/>
    from user
    where
    email = #{email,jdbcType=VARCHAR}
</select>
<insert id="insertAll" parameterType="User" keyProperty="id">
    insert into user
    (id, username, password,
     salt, email, type,
     status, activation_code, header_url,
     create_time)
    values (#{id,jdbcType=NUMERIC}, #{username,jdbcType=VARCHAR}, #{password,jdbcType=VARCHAR},
            #{salt,jdbcType=VARCHAR}, #{email,jdbcType=VARCHAR}, #{type,jdbcType=NUMERIC},
            #{status,jdbcType=NUMERIC}, #{activationCode,jdbcType=VARCHAR}, #{headerUrl,jdbcType=VARCHAR},
            #{createTime,jdbcType=TIMESTAMP})
</insert>
<update id="updateStatusById">
    update user
    set status = #{status,jdbcType=NUMERIC}
    where id = #{id,jdbcType=NUMERIC}
</update>

编写对应注册 html 代码,注意采用 post 提交表单,提交接口为 /register ,并且,为了保证重复填写注册信息,再进行验证的过程中,重复信息不用重复填写,使用th:value="${user!=null?user.username:''}"保证信息验证不通过后,输入框信息不变,进行修改信息直至正确即可。th:class="|form-control ${usernameMsg!=null?'is-invalid':''}|"接受验证信息,通过即用户不存在,不显示错误,否则显示错误,代码如下:

<!-- 关键代码内容 -->
<div class="main">
    <div class="container pl-5 pr-5 pt-3 pb-3 mt-3 mb-3">
        <h3 class="text-center text-info border-bottom pb-3">注&nbsp;&nbsp;册</h3>
        <form class="mt-5" method="post" th:action="@{/register}">
            <div class="form-group row">
                <label for="username" class="col-sm-2 col-form-label text-right">账号:</label>
                <div class="col-sm-10">
                    <input type="text"
                           th:class="|form-control ${usernameMsg!=null?'is-invalid':''}|"
                           th:value="${user!=null?user.username:''}"
                           id="username" name="username" placeholder="请输入您的账号!" required>
                    <div class="invalid-feedback" th:text="${usernameMsg}">
                        该账号已存在!
                    </div>
                </div>
            </div>
            <div class="form-group row mt-4">
                <label for="password" class="col-sm-2 col-form-label text-right">密码:</label>
                <div class="col-sm-10">
                    <input type="password"
                           th:class="|form-control ${passwordMsg!=null?'is-invalid':''}|"
                           th:value="${user!=null?user.password:''}"
                           id="password" name="password" placeholder="请输入您的密码!" required>
                    <div class="invalid-feedback" th:text="${passwordMsg}">
                        密码长度不能小于8位!
                    </div>                            
                </div>
            </div>
            <div class="form-group row mt-4">
                <label for="confirm-password" class="col-sm-2 col-form-label text-right">确认密码:</label>
                <div class="col-sm-10">
                    <input type="password" class="form-control"
                           th:value="${user!=null?user.password:''}"
                           id="confirm-password" placeholder="请再次输入密码!" required>
                    <div class="invalid-feedback">
                        两次输入的密码不一致!
                    </div>
                </div>
            </div>
            <div class="form-group row">
                <label for="email" class="col-sm-2 col-form-label text-right">邮箱:</label>
                <div class="col-sm-10">
                    <input type="email"
                           th:class="|form-control ${emailMsg!=null?'is-invalid':''}|"
                           th:value="${user!=null?user.email:''}"
                           id="email" name="email" placeholder="请输入您的邮箱!" required>
                    <div class="invalid-feedback" th:text="${emailMsg}">
                        该邮箱已注册!
                    </div>
                </div>
            </div>
            <div class="form-group row mt-4">
                <div class="col-sm-2"></div>
                <div class="col-sm-10 text-center">
                    <button type="submit" class="btn btn-info text-white form-control">立即注册</button>
                </div>
            </div>
        </form>                
    </div>
</div>

实现返回注册页面逻辑,用户访问时,使用的是 Get 请求,可与后续提交注册数据的 post 请求区别开,所以访问页面方法和验证注册数据方法都采用同一个路径:/register。返回主页、登录、注册代码类似,仅展示一种,代码如下:

// 注册页面
@RequestMapping(path = "/register", method = RequestMethod.GET)
public String getRegisterPage() {
    return "/site/register";
}

第一阶段以解决,让我们开始实现第二阶段:提交注册数据

首先,我们得先实现三个工具类:

Map<String, Object> map = new HashMap<>();
//空值处理
if (user == null){
    throw new IllegalArgumentException("参数不能为空");
}
if (StringUtils.isBlank(user.getUsername())){
    map.put("usernameMsg", "账号不能为空");
    return map;
}

if (StringUtils.isBlank(user.getPassword())){
    map.put("passwordMsg", "密码不能为空");
    return map;
}

if (StringUtils.isBlank(user.getEmail())){
    map.put("emailMsg", "邮箱不能为空");
    return map;
}
// 验证账号是否存在
User ExistedUser = userMapper.selectOneByUsername(user.getUsername());
System.out.println(ExistedUser != null);
if (ExistedUser != null){
    map.put("usernameMsg","该账号已存在");
    return map;
}

// 验证邮箱是否存在
ExistedUser = userMapper.selectOneByEmail(user.getEmail());
if (ExistedUser!=null){
    map.put("emailMsg","该邮箱已被注册");
    return map;
}

// 注册用户
user.setSalt(CommunityUtil.generateUUID().substring(0,5));
user.setPassword(CommunityUtil.md5(user.getPassword()+user.getSalt()));
user.setType(0);
user.setStatus(0);
//激活码
user.setActivationCode(CommunityUtil.generateUUID());
user.setHeaderUrl(String.format("http://images.nowcoder.com/head/%dt.png)",new Random().nextInt(1000)));
user.setCreateTime(new Date());
userMapper.insertAll(user);

// 发送激活邮件
Context context = new Context();
context.setVariable("email",user.getEmail());
// http://localhost:8888/activation/userId(变量)/激活码(变量)
String url = domain + "/activation/" + user.getId() + "/" + user.getActivationCode();
System.out.println(url);
context.setVariable("url",url);
String content = templateEngine.process("/mail/activation",context);
mailClient.sendMail(user.getEmail(),"激活账号",content);
return map;

接下来,就是最后一个阶段的编写:激活邮件,其实,也很简单,用户点击激活邮件后,会自动跳转到浏览器并自动发送 Get 请求,后端收到请求之后,就要验证 userID 和激活码信息,验证无误后,发送给前端正确信息,并修改数据库中的用户状态为激活即可,否则,就要返回前端错误信息。

为了提高代码可读性,采用验证激活数据常量来辅助代码编写,它们存储在 CommunityConstant.java 中,需要使用 extends CommunityConstant 来让 UserService 继承它,再进行激活操作。

 @RequestMapping(path = "/activation/{userId}/{code}",method = RequestMethod.GET)
public String activation(Model model, @PathVariable("userId") int userId, @PathVariable("code") String code){
    int result = userServiceImpl.activation(userId,code);
    System.out.println("激活返回结果为:"+result);
    if (result == ACTIVATION_NULL){
        model.addAttribute("msg","无效操作!该账号不存在!");
        model.addAttribute("target","/index");
    } else if (result == ACTIVATION_SUCCESS){
        model.addAttribute("msg","激活成功!您的账号已经可以正常使用了!");
        model.addAttribute("target","/login");
    }else if(result == ACTIVATION_REPEAT){
        model.addAttribute("msg","无效操作!该账号已经重复激活了");
        model.addAttribute("target","/index");
    }else {
        model.addAttribute("msg","激活失败!您提供的激活码不正确");
        model.addAttribute("target","/index");
    }
    return "/site/operate-result";
}

前端部分代码较为繁琐,庞大,完整代码可见:一文详解以邮件激活的注册方式 文章资源

当前页面是本站的「Google AMP」版。查看和发表评论请点击:完整版 »