Spring Boot 2.× 对接阿里云短信验证码

好资源吧 3月前 ⋅ 2709 阅读

最终效果

思路

用户输入手机号后,点击按钮获取验证码。并设置冷却时间,防止用户频繁点击。

后台生成验证码并发送到用户手机上,根据验证码、时间及一串自定义秘钥生成MD5值,并将时间也传回到前端。

用户输入验证码后,将验证码和时间传到后台。后台先用当前时间减去前台传过来的时间验证是否超时。如果没有超时,就用用户输入的验证码 + 时间 + 自定义秘钥生成MD5值与之前的MD5值比较,如果相等则验证码校验通过,如果不等则说明验证码输入错误校验失败。 

原理有点像解方程:

xyz经过一种不可逆运算得到A,将y和A传给用户,z后台保留,用户填写x1后,将x1 y A传回后台,后台再用x1 y z经过不可逆运算得到A1,如果A1和A相等,则验证码校验通过。

前端的实现

本例基于BootStrap,html代码中有BootStrap样式。如果你不想用BootStrap,可以将class样式去掉。

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
		<link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
		<link rel="stylesheet" href="/css/ceping/css/main.css">


		<title>测评</title>
	</head>
	<body>
		<div class="container">
			<div class="row">
				<div class="col-lg-4 offset-lg-4 col-md-4 offset-md-4 col-sm-10 offset-sm-1 col-xs-10 offset-xs-1 mainContent" style="background-image: url(/css/ceping/images/indexbg.png);">
					<div class="imgBox">
						<img src="/css/ceping/images/logo_meitu.png" class="indexpic" >
					</div>
					<div class="brief">
						<h3 style="margin-bottom: 80px;">综合能力评测</h3>
					</div>
					<form action="" method="post">
						<div class="input-group">
								
							<div class="input-box">
								<input type="text"  id="phone" name="phone"  placeholder="绑定的手机号" required autocomplete="off">
							</div>
							<div class="input-box">
								<input type="text" id="codes" name="codes" placeholder="验证码"  required autocomplete="off">

								<input type="button" class="btn btn-block btn-flat" id="verify_refresh" onclick="getMsgNum(this)" value="获取验证码">


							</div>
						</div>
					<button class="button" type="button"  onclick="validatenum()">登录</button>
					</form>
					<div style="width: 100%;height: 50px;"></div>
				</div>
			</div>
		</div>
	</body>


	<!-- 全局js -->
	<script src="/js/jquery.min.js?v=2.1.4" th:src="@{/js/jquery.min.js?v=2.1.4}"></script>
	<script src="/js/bootstrap.min.js?v=3.3.6" th:src="@{/js/bootstrap.min.js?v=3.3.6}"></script>
	<script src="/js/plugins/layer/layer.min.js" th:src="@{/js/plugins/layer/layer.min.js}"></script>

	<script type="text/javascript">
		$(function(){
			const windowH=window.innerHeight;
			$('html').css('minHeight',windowH+'px')
			$('body').css('minHeight',windowH+'px')
			$('.container').css('minHeight',windowH+'px')
			$('.container>.row').css('minHeight',windowH+'px')
			$('.container>.row>.mainContent').css('minHeight',windowH+'px')
		})


		var wait = 120; // 短信验证码120秒后才可获取下一个


		/**
		 * 获取验证码
		 * @param that
		 */
		function getMsgNum(that) {


			if ($("#phone").val()=="")
			{
				layer.msg("请输入手机号!");
				return false;
			}


			var phoneNumber = $('#phone').val();
			setButtonStatus(that); // 设置按钮倒计时


			$.ajax({
				type: "POST",
				url: "/test/sendmsg",
				async: false, //false 同步
				data:   {
					phone:$("#phone").val()
				},
				success: function (r) {
					if (r.code==0) {
						layer.msg(r.msg);
					}else{
						layer.msg(r.msg);
					}
				},
			});
		}

		/**
		 * 注册请求
		 * @param that
		 */
		function validatenum() {

			if ($("#phone").val()=="")
			{
				layer.msg("请输入手机号!");
				return false;
			}
			if ($("#codes").val()=="")
			{
				layer.msg("请输入验证码!");
				return false;
			}

			$.ajax({
				type: "POST",
				url: "/test/validatenum",
				data:   {
					phone:$("#phone").val(),
					codes:$("#codes").val()
				},
				success: function (r) {
					if (r.code==0) {
						layer.msg(r.msg);

						window.setTimeout(window.location.href="/test/index",5000);




					}else{
						layer.msg(r.msg);
					}
				},
			});
		}




		/**
		 * 设置按钮状态
		 */
		function setButtonStatus(that) {
			if (wait == 0) {
				that.removeAttribute("disabled");
				that.value="获取验证码";
				wait = 60;
			} else {
				that.setAttribute("disabled", true);
				that.value=wait+"S";
				wait--;
				setTimeout(function() {
					setButtonStatus(that)
				}, 1000)
			}
		}




	</script>



</html>

后台的实现

 @ResponseBody()
    @PostMapping("/sendmsg")
    public R sendMsg(HttpServletRequest request, SmsDto smsDto) {

        JSONObject json = null;

        try {

            SendSmsResponse sendSmsResponse = AliyunConfig.sendSms(smsDto.getPhone());


            if (sendSmsResponse.getMessage()=="1")
            {

                //将验证码存到session中,同时存入创建时间
                //以json存放,这里使用的是阿里的fastjson

                json = new JSONObject();
                json.put("verifyCode", sendSmsResponse.getCode());
                json.put("createTime", System.currentTimeMillis());
                // 将认证码存入SESSION
                request.getSession().setAttribute("verifyCode", json);

                return R.ok("发送成功!");
            }
            else
            {
                return R.error("发送失败!");
            }

        } catch (ClientException e) {

            return R.error("发送失败!"+e.getMessage());

        } catch (com.aliyuncs.exceptions.ClientException e) {
            return R.error("发送失败!"+e.getMessage());
        }

    }

    @ResponseBody
    @PostMapping("/validatenum")
    public R validateNum(HttpServletRequest request, HttpServletResponse response, SmsDto smsDto) {

        JSONObject json = (JSONObject)request.getSession().getAttribute("verifyCode");

        if (json==null)
        {
            return R.error("验证码错误");
        }

        if(!json.getString("verifyCode").equals(smsDto.getCodes())){
            return R.error("验证码错误");
        }
        if((System.currentTimeMillis() - json.getLong("createTime")) > 1000 * 60 * 5){
            return R.error("验证码过期");
        }



        Map<String,Object> map = new HashMap<>();
        map.put("mobile",smsDto.getPhone());
        int count = cpuserService.count(map);


        if (count>0)//如果根据手机已经注册
        {


            HttpSession session = request.getSession();
            session.setAttribute("mobile", smsDto.getPhone());

            session.setMaxInactiveInterval(100000);
            

            return R.ok("注册成功!");
        }
        else
        {
            CpuserDO cpuserDO = new CpuserDO();

            cpuserDO.setCreatetime(new Date());

            cpuserDO.setMobile(smsDto.getPhone());

            cpuserDO.setLogintime(new Date());

            int save = cpuserService.save(cpuserDO);
            if (save>0)
            {

                HttpSession session = request.getSession();
                session.setAttribute("mobile", smsDto.getPhone());
                session.setMaxInactiveInterval(100000);

                return R.ok("注册成功!");
            }
            else
            {
                return R.error("注册失败!");
            }
        }

    }
    

运用到的公用类

public class AliyunConfig {



        /* 短信API产品名称(短信产品名固定,无需修改) */

        private static final String product = "Dysmsapi";



        /* 短信API产品域名,接口地址固定,无需修改 */

        private static final String domain = "dysmsapi.aliyuncs.com";



        /* 此处需要替换成开发者自己的accessKeyId和accessKeySecret(在阿里云访问控制台寻找) */

        private static final String accessKeyId = "xxx"; //TODO: 这里要写成你自己生成的

        private static final String accessKeySecret = "xxx";//TODO: 这里要写成你自己生成的



        /* 短信发送 */

        public static SendSmsResponse sendSms(String phone) throws ClientException {



            /* 超时时间,可自主调整 */

            System.setProperty("sun.net.client.defaultConnectTimeout", "50000");

            System.setProperty("sun.net.client.defaultReadTimeout", "50000");



            /* 初始化acsClient,暂不支持region化 */

            IClientProfile profile = DefaultProfile.getProfile("cn-hangzhou", accessKeyId, accessKeySecret);

            DefaultProfile.addEndpoint("cn-hangzhou", "cn-hangzhou", product, domain);

            IAcsClient acsClient = new DefaultAcsClient(profile);


            /* 组装请求对象-具体描述见控制台-文档部分内容 */

            SendSmsRequest request = new SendSmsRequest();

            /* 必填:待发送手机号 */

            request.setPhoneNumbers(phone);

            /* 必填:短信签名-可在短信控制台中找到 */

            request.setSignName("xxx"); //TODO: 这里是你短信签名

            /* 必填:短信模板code-可在短信控制台中找到 */

            request.setTemplateCode("xxx"); //TODO: 这里是你的模板code

            /* 可选:模板中的变量替换JSON串,如模板内容为"亲爱的用户,您的验证码为${code}"时,此处的值为 */

            String getscode = getMsgCode();

            request.setTemplateParam("{\"code\":\"" + getscode + "\"}");

            // request.setTemplateParam("{\"code\":\"" + getMsgCode() + "\"}");

            // hint 此处可能会抛出异常,注意catch

            SendSmsResponse sendSmsResponse = acsClient.getAcsResponse(request);

            if(sendSmsResponse.getCode()!= null && sendSmsResponse.getCode().equals("OK")){

                // System.out.println("短信发送成功!验证码:" + getscode);
                sendSmsResponse.setMessage("1");
                sendSmsResponse.setCode(getscode);

            }else {
                sendSmsResponse.setMessage("0");
            }

            return sendSmsResponse;
        }


        /**

         * @Function: 生成验证码

         * @author:   yangxf

         * @Date:     2019/4/11 15:30

         */

        private static String getMsgCode() {

            int n = 6;
            StringBuilder code = new StringBuilder();
            Random ran = new Random();

            for (int i = 0; i < n; i++) {

                code.append(Integer.valueOf(ran.nextInt(10)).toString());

            }
            return code.toString();
        }

大家记得别忘记加入阿里云包依赖

        <dependency>
            <groupId>com.aliyun</groupId>
            <artifactId>aliyun-java-sdk-sts</artifactId>
            <version>3.0.0</version>
        </dependency>

        <dependency>
            <groupId>com.aliyun</groupId>
            <artifactId>aliyun-java-sdk-core</artifactId>
            <version>3.5.0</version>
        </dependency>


        <dependency>
            <groupId>com.aliyun.oss</groupId>
            <artifactId>aliyun-sdk-oss</artifactId>
            <version>2.8.3</version>
        </dependency>

        <dependency>
            <groupId>com.aliyun</groupId>
            <artifactId>aliyun-java-sdk-dysmsapi</artifactId>
            <version>1.0.0</version>
        </dependency>

这样就完成了Spring Boot 短信验证和授权,大家如果有什么疑问可以留言,或者有什么问题可以提出来,一同探讨,谢谢大家!


全部评论: 0

    我有话说: