# 目标 - 完成一个手机验证码功能

要求:
1、输入手机号,点击发送后随机生成 6 位数字码,2 分钟有效
2、输入验证码,点击验证,返回成功或失败
3、每个手机号每天只能输入 3 次

image-20220731164343801

实现:

# 前端代码:

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<script  src="static/jquery/jquery-3.1.0.js" ></script>
<link href="static/bs/css/bootstrap.min.css" rel="stylesheet" />
<script  src="static/bs/js/bootstrap.min.js"   ></script>
</head>
<body>
   <div class="container">
		<div class="row">
		    <div id="alertdiv" class="col-md-12">
		          <form class="navbar-form navbar-left" role="search" id="codeform">
				  <div class="form-group">
				    <input type="text" class="form-control" placeholder="填写手机号" name="phone_no">
				    <button type="button" class="btn btn-default" id="sendCode">发送验证码</button><br>
				    <font id="countdown" color="red" ></font>
				    <br>
				    <input type="text" class="form-control" placeholder="填写验证码" name="verify_code">
				    <button type="button" class="btn btn-default" id="verifyCode">确定</button>
				    <font id="result" color="green" ></font><font id="error" color="red" ></font>
				    </div>
				    </form>
    </div>
   </div>
  </div>
</body>
<script type="text/javascript"> 
var t=120;// 设定倒计时的时间 
var interval;
function refer(){  
    $("#countdown").text("请于"+t+"秒内填写验证码 "); // 显示倒计时 
    t--; // 计数器递减 
    if(t<=0){
    	clearInterval(interval);
    	$("#countdown").text("验证码已失效,请重新发送! ");
    }
} 
$(function(){
	$("#sendCode").click( function () {
	       
		   $.post("SendCodeServlet",$("#codeform").serialize(),function(data){
	    	 if(data=="true"){
	    		 t=120;
	    		 clearInterval(interval);
	    		 interval= setInterval("refer()",1000);// 启动 1 秒定时  
	   		 }else if (data=="limit"){
	   			clearInterval(interval);
	   			$("#countdown").text("单日发送超过次数! ")
	   		 }
		  });   
    });
	
	$("#verifyCode").click( function () {
	    
		   $.post("CheckCodeServlet",$("#codeform").serialize(),function(data){
	    	 if(data=="true"){
	    		 $("#result").attr("color","green");
	    		 $("#result").text("验证成功");
	    		 clearInterval(interval);
	    		 $("#countdown").text("")
	   		 }else{
	    		 $("#result").attr("color","red");
	    		 $("#result").text("验证失败");
	   		 }
		  });   
    });
	
	
});
</script>
</html>

# 后端代码:

  1. 点击发送验证码时:

将数据存入 redis 中

package com.dabing.redis.servlet;
import redis.clients.jedis.Jedis;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.time.Duration;
import java.time.LocalTime;
import java.util.Random;
// 处理发送验证码的 Servlet
@WebServlet("/SendCodeServlet")
public class SendCodeServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String phoneNo = request.getParameter("phone_no");
        if(phoneNo==null || "".equals(phoneNo)){
            return;
        }
        Jedis jedis = new Jedis("xxx.xxx.xx.xx",6379);
        String countKey=phoneNo+":count";  // 生成验证码的次数 key
        String codeKey=phoneNo+":code";    // 手机验证码 key
        String count = jedis.get(countKey);
        String code=getCode(6);
        System.out.println("code="+code);
        if(count==null){
            // 证明没有发送过验证码
            // 保存验证码到 redis,过期时间 120 秒
            jedis.setex(codeKey,120,code);
            // 设置过期时间为今天的剩余时间
            jedis.setex(countKey,(int)getTheLeftSeconds(),"1");
            response.getWriter().write("true");
        }else if ("3".equals(count)){
            // 证明已经发送过 3 次了
            response.getWriter().write("limit");
            return;// 返回不让发了
        }else {
            // 证明已经发过,但是次数没达到 3 次
            jedis.setex(codeKey,120,code);
            jedis.incr(countKey);// 次数加 1
            response.getWriter().write("true");
        }
        jedis.close();
    }
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doPost(request,response);
    }
    // 随机生成验证码的方法
    private String getCode(int len) {
        String code = "";
        for (int i = 0; i < len; i++) {
            int rand = new Random().nextInt(10);
            code += rand;
        }
        return code;
    }
    // 获取当天剩余秒数的方法
    private long getTheLeftSeconds(){
        // 获取现在的时间
        LocalTime now = LocalTime.now();
        // 获取当日 23 点 59 分 59 秒的时间
        LocalTime end = LocalTime.of(23, 59, 59);
        // 获取 end 与 now 相差的秒数
        long millis = Duration.between(now, end).toMillis()/1000;
        return millis;
    }
}
  1. 输入验证码校验时

从 redis 中取出对比

package com.dabing.redis.servlet;
import redis.clients.jedis.Jedis;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
// 处理验证验证码请求的 Servlet
@WebServlet("/CheckCodeServlet")
public class CheckCodeServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String code=request.getParameter("verify_code");
        String phoneNo=request.getParameter("phone_no");
        if(code==null||phoneNo==null||"".equals(code)|| "".equals(phoneNo)){
            return;
        }
        Jedis jedis = new Jedis("xxx.xx.xxx.xx",6379);
        String codeKey=phoneNo + ":code";
        String codeRedis = jedis.get(codeKey);
        if(code.equals(codeRedis)){
            jedis.del(codeKey);
            response.getWriter().write("true");
        }
        jedis.close();
    }
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doPost(request,response);
    }
}