博客郵箱驗證(轉)

 

注册后端流程

流程如下图所示:

  • 1. 用户点击注册提交。根据 URL 映射到具体的 Controller 的具体方法。

  • 2. 后台再次判断验证码是否正确,错误则返回注册页面,提示错误。

  • 3. 验证码正确后:

    • 3.1 将 MD5 随机生成的激活码保存到 Redis 中,key 为 email,value 为激活码,并且设置保存时间为24小时;
    • 3.2 将用户的信息封装到 user 对象以后保存到 MySQL 数据库中;
    • 3.3 然后给注册用户发送激活邮件,并跳转到注册成功页面。
  • 4. 用户激活:
    • 4.1 在24小时内激活,跳转到激活成功页面;
    • 4.2 在24小时内激活,激活码错误跳转到激活失败页面(比如网页链接上自行修改激活码测试);
    • 4.3 超过24小时,Redis 删除用户激活码信息。
    • 4.4 超过24小时激活,激活失败,MySQL 删除用户未激活注册信息。

根据流程可知需要用到 Redis 和发送邮件功能,这里先简单介绍下 Redis 及如何开通邮箱的 POP3/SMTP 服务。

Redis 简介

Redis 是一个高性能的 key-value 型数据库。Redis 支持数据的持久化,可以将内存中的数据保存在磁盘中,重启的时候可以再次加载进行使用。

Redis 不仅仅支持简单的 key-value 类型的数据,同时还提供 List、Set、Zset、Hash等数据结构的存储。

Redis 支持数据的备份,即 master-slave 模式的数据备份。

我已将 Redis 压缩包放在了文末的百度网盘链接中,也可自己下载。

Redis 压缩包解压后,进入目录,点击 redis-server.exe,启动 Redis,然后在同目录下启动 redis-cli.exe 从而启动 Redis 客户端,命令如下:

输入:

    set name wly

通过:

    get name

即可获得存入 name 中的值,如图:

Java 中是通过 Redis 模板操作 Redis 的,pom.xml 中已经导入了依赖包。这里主要将 Spring 和 Redis 的整合配置文件 applicationContext-redis.xml 和 Redis 的配置文件 redis.properties 进行配置。

1.applicationContext-redis.xml 配置如下:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
        <property name="maxIdle" value="300" />
        <property name="maxWaitMillis" value="3000" />
        <property name="testOnBorrow" value="true" />
    </bean>
    <!-- 从外部配置文件获取redis相关信息 -->
    <bean id="redisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
        <property name="hostName" value="${redis.ip}" />
        <property name="port" value="${redis.port}" />
        <property name="database" value="${redis.database}" />
        <property name="poolConfig" ref="poolConfig"/>
    </bean>
    <!-- redis模板配置 -->
    <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
        <property name="connectionFactory" ref="redisConnectionFactory"></property>
        <!-- 对于中文的存储 需要进行序列化操作存储  -->
        <property name="keySerializer">
            <bean class="org.springframework.data.redis.serializer.StringRedisSerializer" />
        </property>
        <property name="valueSerializer">
            <bean class="org.springframework.data.redis.serializer.StringRedisSerializer">
            </bean>
        </property>
    </bean>
    </beans>

代码解读如下:

(1)连接池的配置。

获取最大空闲连接数:

    <property name="maxIdle" value="300" />

获取连接时的最大等待毫秒数:

    <property name="maxWaitMillis" value="3000" />

在获取连接的时候检查有效性:

    <property name="testOnBorrow" value="true" />

(2)Redis 连接工厂配置。

主机名:

    <property name="hostName" value="${redis.ip}" />

端口号:

    <property name="port" value="${redis.port}" />

选用的数据库:

    <property name="database" value="${redis.database}" />

连接池名称,引用上面配置的连接池:

    <property name="poolConfig" ref="poolConfig"/>

(3)redisTemplate 模板配置,主要是将 Redis 模板交给 Spring 管理、引入上面配置的 Redis 连接工厂,对中文存储进行序列化操作等。

2.redis.properties 配置如下:

    redis.pool.maxActive=1024
    redis.pool.maxIdle=200
    redis.pool.maxWait=1000
    redis.ip=localhost
    redis.port=6379
    redis.database=2

redis.properties 就是属性配置文件,applicationContext-redis.xm 中连接池的配置就是从这里取的值,其中 redis.database=2 代表选用第二个数据库。

3.Redis 相关配置文件配置好以后,引入项目中:

web.xml中引入applicationContext-redis.xml:

    <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>
      classpath*:spring-mybatis.xml,
      classpath*:applicationContext-redis.xml
    </param-value>
     </context-param>

spring-mybatis.xml 中引入 redis.properties:

     <bean id="configProperties" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
        <property name="locations">
            <list>
                <value>classpath:jdbc.properties</value>
                <value>classpath:redis.properties</value>
            </list>
        </property>
        <property name="fileEncoding" value="UTF-8"/>
    </bean>

classpath 表示只会到你的 class 路径中查找文件。

classpath*:不仅在 class 路径,还会在 jar 文件中(class 路径)进行查找。

所以 classpath* 的加载速度要慢,通常情况下使用 classpath,找不到文件的情况时才使用 classpath*

开通邮箱的 POP3/SMTP 服务

这里以网易163邮箱为例。

登入163邮箱后,点击设置中的 POP3/SMTP/IMAP 开通 POP3/SMTP 服务,然后点击客户端授权密码,获取一个授权密码,之后的发送邮件功能中需要用到,如下图:

项目中还会用到 MD5 加密,在 common 包下引入 MD5Util 工具类,该工具类我也放在了百度网盘链接中。

注册后台代码

在 RegisterController.java 中创建映射 URL 为 /doRegister 的方法:

    @Autowired// redis数据库操作模板
    private RedisTemplate<String, String> redisTemplate;
    @RequestMapping("/doRegister")
    public String doRegister(Model model, @RequestParam(value = "email", required = false) String email,
                             @RequestParam(value = "password", required = false) String password,
                             @RequestParam(value = "phone", required = false) String phone,
                             @RequestParam(value = "nickName", required = false) String nickname,
                             @RequestParam(value = "code", required = false) String code) {

        log.debug("注册...");
        if (StringUtils.isBlank(code)) {
            model.addAttribute("error", "非法注册,请重新注册!");
            return "../register";
        }

        int b = checkValidateCode(code);
        if (b == -1) {
            model.addAttribute("error", "验证码超时,请重新注册!");
            return "../register";
        } else if (b == 0) {
            model.addAttribute("error", "验证码不正确,请重新输入!");
            return "../register";
        }


        User user = userService.findByEmail(email);
        if (user != null) {
            model.addAttribute("error", "该用户已经被注册!");
            return "../register";
        } else {
            user = new User();
            user.setNickName(nickname);

            user.setPassword(MD5Util.encodeToHex("salt"+password));
            user.setPhone(phone);
            user.setEmail(email);
            user.setState("0");
            user.setEnable("0");
            user.setImgUrl("/images/icon_m.jpg");
            //邮件激活码
            String validateCode = MD5Util.encodeToHex("salt"+email + password);
            redisTemplate.opsForValue().set(email, validateCode, 24, TimeUnit.HOURS);// 24小时 有效激活 redis保存激活码

            userService.regist(user);

            log.info("注册成功");
            SendEmail.sendEmailMessage(email, validateCode);
            String message = email + "," + validateCode;
            model.addAttribute("message", message);
            return "/regist/registerSuccess";

        }
    }
        // 匹对验证码的正确性

    public int checkValidateCode(String code) {
        ServletRequestAttributes attrs = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        Object vercode = attrs.getRequest().getSession().getAttribute("VERCODE_KEY");
        if (null == vercode) {
            return -1;
        }
        if (!code.equalsIgnoreCase(vercode.toString())) {
            return 0;
        }
        return 1;
    }

代码解读如下:

(1)通过 @Autowired 注解注入 RedisTemplate 模板,用于操作 Redis。

(2)判断验证码是否为空,如果为空,通过 model.addAttribute 方法将错误信息添加到 model 中,前台可通过键“error”获取对应的值,并返回到注册页面。

这里return "../register" 是根据之前 spring-mvc.xml 中配置的规则转发的,如下:

    <!-- 定义跳转的文件的前后缀 ,视图模式配置 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/" />
        <property name="suffix" value=".jsp"/>
    </bean>

前缀为/WEB-INF/../ 代表退回到上一层,即到了 /webapp/register 下,后缀是.jsp

那么最终结果就是 return 到了 /webapp/register.jsp 注册页面。

(3)这里封装了匹配验证码正确性的方法 checkValidateCode,如果 Session 中不存在验证码,则返回-1,提示超时错误,如果存在验证码但是和前台传入的验证码不一致则返回0,提示验证码错误,否则返回1,验证码正确,不提示。

(4)根据前台传来的 email 查询用户是否存在,如果存在说明已经注册,否则给予错误提示,返回注册页面。

(5)用户不存在,这里新创建一个 User,将前台传来的信息赋值给 User,通过 MD5 工具类对密码进行加密,其中 salt 用来增加密码的复杂度,将 state 和 enable 赋初始值0,state 用于用户的激活,激活后变为1,enable 属性以后会说,给用户设置一个默认的头像。

(6)通过 MD5 加密工具生成唯一的邮件激活码赋值给 validateCode。

(7)通过 redisTemplate.opsForValue().set 方法将激活码 validateCode 存入 Redis,以 email 为键,validateCode 为值,设置有效时间为24,单位为小时,即激活码时效为一天。

(8)调用 userService 的 regist 方法将用户插入数据库。

(9)调用发送邮件的方法发送邮件,参数是发送邮件的目的地和激活码。

(10)将邮箱和激活码用 , 号拼接后赋值给 message,将 message 添加到 model 中。

(11)返回注册成功页面 registerSuccess.jsp。

发送邮件类

在 wang.dreamland.www 路径下新建 mail 包并引入发送邮件的相关类,mail 包我也放在了文末的百度网盘链接中,主要有三个类:

  1. MailExample.java 用于测试;
  2. MyAuthenticator.java 用于验证邮箱和授权码的正确性;
  3. SendEmail.java 用于发送邮件的,主要注意将发件人的邮箱改成你的邮箱,还有输入你的授权码。

注意: 启动项目前如果 Redis 没有启动,请先启动 redis-server.exe。

重新启动项目,注册一个账号,注册成功后跳转到注册成功页面,如下图:

激活流程

1.点击立即查看邮箱,可根据不同邮箱跳转到不同邮箱主页。

    <div class="register-active">
           <span style="font-size: 15px;font-weight: bold;padding: 20px;line-height: 100px">激活邮件已经发送到您的注册邮箱${message.split(",")[0]},点击邮件里的链接即可激活账号。</span><br/>
            <button style="margin-left: 20px;" class="btn btn-primary" type="button" onclick="lookEmail('${message}');">立即查看邮件</button>
     </div>

根据键 message 取出后台存入 model 中的值,然后根据 , 号切割,取第一个元素就是邮箱了。

查看邮箱的点击事件 lookEmail 方法如下:

    function lookEmail(message) {
        var arr = message.split(",");
        var email = arr[0];
        var opt = email.split("@")[1];
        if("qq.com"==opt){
            location.href = "https://mail.qq.com/";
        }else if("163.com"==opt){
                location.href = "https://mail.163.com/";
        }else if("162.com"==opt){
            location.href = "https://mail.162.com/";
        }else if("sina.com"==opt){
            location.href = "http://mail.sina.com.cn/";
        }else if("sohu"==opt){
            location.href = "https://mail.sohu.com";
        }
    }

代码解读如下:

(1)参数 message 是邮箱和激活码通过 , 号拼接的字符串,根据 , 号切割取出邮箱。

(2)根据 @ 切割取出邮箱 @ 后面部分,主要根据 @ 后面的信息进行跳转。

(3)如果后面部分是 qq.com 则链接到 QQ 邮箱,其他同理。

2.点击重新发送邮件,可重新发送激活邮件。

    onclick="reSendEmail('${message}')" 

点击事件 reSendEmail 方法如下:

     function reSendEmail(message) {
       var arr = message.split(",");
       var email = arr[0];
       var code = arr[1];
        $.ajax({
            type:'post',
            url:'/sendEmail',
            data: {"email":email,"validateCode":code},
            dataType:'json',
            success:function(data){
                //alert(data["success"])
                var s = data["success"];
                if(s=="success"){
                    alert("发送成功!");
                }
            }
        });
    }

代码解读如下:

(1)通过 , 号切割分别拿到邮箱和激活码。

(2)发送 AJAX 请求,映射 URL 为 /sendEmail,请求参数 data 是邮箱和激活码。

(3)success 回调函数返回后台处理结果。如果是“success”代表发送成功,给予提示。

后台 RegisterController.java 中映射 URL 为 /sendEmail 的方法如下:

    @RequestMapping("/sendEmail")
    @ResponseBody
    public  Map<String,Object> sendEmail(Model model) {
        Map map = new HashMap<String,Object>(  );
        ServletRequestAttributes attrs = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        String validateCode = attrs.getRequest().getParameter( "validateCode" );
        String email = attrs.getRequest().getParameter( "email" );
        SendEmail.sendEmailMessage(email,validateCode);
        map.put( "success","success" );
        return map;
    }

代码解读如下:

(1)这里我使用的是另一种获取参数的方式,通过 request.getParameter() 方法也可以获取前台传来的参数,参数是要获取的参数名称。

(2)调用发送邮件的方法即可,然后将“success”放入 map 集合,返回给前台。

3.重新注册,跳转到注册页面。

点击事件 reRegist 方法如下:

     function reRegist() {
         location.href = "../register.jsp";
    }

通过 location.href 属性链接到 register.jsp 页面。

也可以通过后台 Controller 跳转:

    function reRegist() {
         location.href = "${ctx}/register";
    }

RegisterController.java 写上对应 URL 的方法:

    @RequestMapping("/register")
    public String register(Model model) {

        log.info("进入注册页面");

        return "../register";
    }

4.进入邮箱进行激活。

(1)登录邮箱打开收到的激活邮件,点击“请于24小时内点击激活”链接:

点击后获得的链接也就是我们发送邮件方法里面写的链接:

     message.setContent( "<a href=\"http://localhost:8080/activecode?email="+email+"&validateCode="+validateCode+"\" target=\"_blank\">请于24小时内点击激活</a>","text/html;charset=gb2312");

在 RegisterController.java 创建映射 URL 为 /activecode 的方法如下:

    @Autowired
    private RoleUserService roleUserService;
    @RequestMapping("/activecode")
    public String active(Model model) {
        log.info( "==============激活验证==================" );
        //判断   激活有无过期 是否正确
        //validateCode=
        ServletRequestAttributes attrs = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        String validateCode = attrs.getRequest().getParameter( "validateCode" );
        String email = attrs.getRequest().getParameter( "email" );
        String code = redisTemplate.opsForValue().get( email );
        log.info( "验证邮箱为:"+email+",邮箱激活码为:"+code+",用户链接的激活码为:"+validateCode );
        //判断是否已激活

        User userTrue = userService.findByEmail( email );
        if(userTrue!=null && "1".equals( userTrue.getState() )){
            //已激活
            model.addAttribute( "success","您已激活,请直接登录!" );
            return "../login";
        }

        if(code==null){
            //激活码过期
            model.addAttribute( "fail","您的激活码已过期,请重新注册!" );
            userService.deleteByEmail( email );
            return "/regist/activeFail";
        }

        if(StringUtils.isNotBlank( validateCode ) && validateCode.equals( code )){
            //激活码正确
            userTrue.setEnable( "1" );
            userTrue.setState( "1" );
            userService.update( userTrue );
            model.addAttribute( "email",email );
            return "/regist/activeSuccess";
        }else {
            //激活码错误
            model.addAttribute( "fail","您的激活码错误,请重新激活!" );
            return "/regist/activeFail";
        }

    }

代码解读如下:

A. 这里通过 request.getParameter 方法获取请求参数:邮箱和激活码。

B. 通过键 email 去 Redis 中取出之前存入 Redis 中的激活码,赋值给 code。

C. 根据邮箱查询用户,如果用户不为 null 并且激活状态 state=1 说明该用户已经激活,将“success”添加到 model 中并跳转到登录页面。

D. 如果从 Redis 中取出的激活码为 null,说明激活码已经超过24小时,将“fail”添加到 model 中,然后 根据用户 email 删除用户,返回注册页面。

E. 如果前台传来的激活码不为空并且和 Redis 中取出的激活码相同说明激活码正确,更新用户激活状态 state 和 enable 为1,将 email 添加到 model 中,返回到激活成功页面,否则将“fail”添加到 model 中并返回到激活失败页面。

重新启动项目,注册 -> 注册成功 -> 进入邮箱点击激活链接。

(2)激活成功页面效果如下:

(3)激活失败页面效果如下:

注意:

激活成功后,发现数据库中 state 和 enable 字段并未更新,因为 User 实体类的 id 我们没有给它标识为主键,所以没有根据主键来更新字段。

在 id 字段上加上以下注解:

    @Id//标识主键
    @GeneratedValue(strategy = GenerationType.IDENTITY) //自增长策略

顺便将其他实体类一并加上这两个注解,然后再启动项目,点击激活链接,查看数据库,字段已更新!

 

-------------

這一段,純抄,不會就是這麽乾脆。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值