前端功能问题系列文章,点击上方合集↑
序言
大家好,我是大澈!
本文约3400+
字,整篇阅读大约需要4分钟。
本文主要内容分三部分,第一部分是需求分析,第二部分是实现步骤,第三部分是问题详解。
如果您只需要解决问题,请阅读第一、二部分即可。
如果您有更多时间,进一步学习问题相关知识点,请阅读至第三部分。
1. 需求分析
点击发送验证码按钮,获取手机短信验证码。此时,发送验证码按钮进入倒计时状态,且不可被点击。
成功获取到短信验证码并输入,点击登录按钮,完成页面跳转。
2. 实现步骤
2.1 准备工作
短信验证码登录功能的实现,借助了阿里云短信业务API,我们可以使用阿里云的短信服务向用户发送验证码、通知、营销等不同类型的短信。
总体实现步骤如下:
-
创建阿里云账号
:如果您还没有阿里云账号,需要先注册一个账号并完成身份验证。 -
开通短信服务
:登录阿里云控制台,找到短信服务并进行开通,然后完善相关信息,包括资质、签名、模板等,最后还需要购买短信套餐包。这里解释一下两个概念:
短信签名
:短信签名是您发送短信时用于展示发送方身份的标识,需要在阿里云短信控制台进行申请和审核。短信模板
:短信模板是您发送短信时所使用的短信内容模板,需要在阿里云短信控制台进行申请和审核。
-
获取API凭证
:在阿里云控制台中,创建 AccessKey,并确保该密钥对拥有短信发送的权限。 -
编写后端接口调用对应API
:使用选择的编程语言和相关 SDK,调用阿里云短信业务API中相应的接口,如发送短信、查询发送记录等。 -
编写前端代码
:发送短信并验证登录。
下面是具体的代码实现,包括前端、后端
两方面。
2.2 编写前端代码
对于前端来说,所做的操作并不算多,无非是要实现点击发送短信验证码后的倒计时效果,以及两个接口的调用。
对于倒计时效果,就是利用了定时器,去控制发送验证码按钮的状态。
对于调用的两个接口,一个是发送短信接口,一个是短信验证及登录接口。
相关代码实例请继续往下看,每个地方基本都给大家做了详细的注释。
模版代码:
<template>
<div class="box">
<el-form :model="dataForm" label-width="120px">
<el-form-item label="手机号:">
<el-input v-model="dataForm.phone" />
</el-form-item>
<el-form-item label="验证码:">
<el-input v-model="dataForm.code" />
<el-button type="warning" @click="sendVerificationCode" :disabled="isSendingCode || countdown > 0">
{{ countdown > 0 ? `重新发送(${countdown})` : '发送验证码' }}
</el-button>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="onSubmit" >登录</el-button>
</el-form-item>
</el-form>
</div>
</template>
逻辑代码:
<script setup>
import {reactive, ref} from 'vue'
import Axios from '../api/axios';
import {ElMessage} from "element-plus";
const dataForm = reactive({
phone: '123456',
code: '',
})
let isSendingCode = ref(false);
let countdown = ref(0);
// 发送验证码,调验证码接口
const sendVerificationCode = () => {
// 检查手机号是否有效
// ...
// 调发送短信接口
Axios.get('/admin/send', {
params: {
phone: dataForm.phone,
}
})
.then(res => {
ElMessage.success(res.data)
})
.catch(error => {
console.error(error);
});
isSendingCode.value = true;
// 设置倒计时时间,这里假设为10秒
countdown.value = 10;
// 倒计时效果
const countdownInterval = setInterval(() => {
countdown.value--;
if (countdown.value <= 0) {
clearInterval(countdownInterval);
isSendingCode.value = false;
}
}, 1000);
}
// 登录,调检验验证码和密码的登录接口
const onSubmit = () => {
// 调发送短信接口
Axios.post('/admin/checkLogin', {
phoneNumber: dataForm.phone,
code: dataForm.code,
})
.then(res => {
ElMessage.success(res.data)
})
.catch(error => {
console.error(error);
});
}
</script>
2.3 编写后端接口
对于后端实现,要先引入阿里云短信业务依赖,再封装短信发送工具类,再编写两个接口,一个是发送短信接口,一个是短信验证及登录接口。
在发送短信接口业务层中,生成随机字符串,传入并调用短信发送工具类方法发送验证码,并把验证码存入Redis
中。
在短信验证及登录接口业务层中,将前端传过来的验证码与Redis
中存的验证码进行比较,校验成功执行下一步登录操作。
相关代码实例请继续往下看,每个地方基本都给大家做了详细的注释。
Pom.xml
中引入依赖代码:
<!--redis-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- 阿里短信包 -->
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-core</artifactId>
<version>4.6.0</version>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-dysmsapi</artifactId>
<version>1.1.0</version>
</dependency>
<!-- json -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.76</version>
</dependency>
短信发送服务工具类代码:
package com.dache.base.common.util;
import com.alibaba.fastjson.JSONObject;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.dysmsapi.model.v20170525.SendSmsRequest;
import com.aliyuncs.dysmsapi.model.v20170525.SendSmsResponse;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.exceptions.ServerException;
import com.aliyuncs.profile.DefaultProfile;
import com.google.gson.Gson;
import java.util.HashMap;
/**
* @Description 短信发送服务
*/
public class SmsUtil {
public static String toSendMes(String phoneNumber,String code) {
//1.连接阿里云
/**
"<your-region-id>", // The region ID 地区标识
"<your-access-key-id>", // The AccessKey ID of the RAM account RAM账户的AccessKey ID 阿里云账号可查
"<your-access-key-secret>", // The AccessKey Secret of the RAM account RAM 账户的 AccessKey Secret 阿里云账号可查
**/
DefaultProfile profile = DefaultProfile.getProfile("cn-beijing", "your-access-key-id", "your-access-key-secret");
IAcsClient client = new DefaultAcsClient(profile);
//2.构建请求 自定义参数
SendSmsRequest request = new SendSmsRequest();
//接收短信的手机号码
request.setPhoneNumbers(phoneNumber);
//短信签名名称
request.setSignName("阿里云短信测试");
//短信模板CODE
request.setTemplateCode("SMS_154SECRET9");
//模版内容:您正在使用阿里云短信测试服务,体验验证码是:${code},如非本人操作,请忽略本短信!
//短信模板变量对应的实际值
//("{\"code\":\"1234\"}");
HashMap<String,String> param=new HashMap<>();
param.put("code",code);
request.setTemplateParam(JSONObject.toJSONString(param));
SendSmsResponse response = new SendSmsResponse();
try {
//3.发送请求
response = client.getAcsResponse(request);
} catch (ServerException e) {
e.printStackTrace();
} catch (ClientException e) {
System.out.println("ErrCode:" + e.getErrCode());
System.out.println("ErrMsg:" + e.getErrMsg());
System.out.println("RequestId:" + e.getRequestId());
}
//短信成功返回json { "RequestId": "614048FB-0619-4439-A1D5-AA8B218A****", "Message": "OK", "BizId": "386715418801811068^0", "Code": "OK"}
return response.getMessage();
}
}
接口Controller
层代码:
/**
* 发送短信
*/
@ApiOperation(value = "发送短信")
@GetMapping("/send")
public CommonResult<String> toSendMessage(@RequestParam("phone") String phone){
String message = adminService.toSendMessage(phone);
return CommonResult.success(message);
}
/**
* 检验验证码和密码,校验成功后登录
*/
@ApiOperation(value = "检验验证码和密码,校验成功后登录")
@PostMapping("/checkLogin")
public CommonResult<String> checkLogin(@RequestBody ToCheckLoginDTO toCheckLogin){
String checkLogin = adminService.checkLogin(toCheckLogin.getPhoneNumber(), toCheckLogin.getCode());
// 省略用户密码加密校验
// ...
// 省略JWT认证
// ...
return CommonResult.success(checkLogin);
}
接口Service
层代码:
/**
* 发送短信
*/
@Override
public String toSendMessage(String phoneNumber) {
//扩展 可以验证该电话号码是否注册
//1.判定验证码是否过期
String code = redisTemplate.opsForValue().get(phoneNumber);
if (!StringUtils.isEmpty(code)){
return phoneNumber+":"+"验证码未过期";
}
//2.已过期/无验证码 生成验证码
//随机生成字符串 做验证码
int toCode = (int) (Math.random() * (50000 - 40000) + 40000);
code=Integer.toString(toCode);
String toSendMes = SmsUtil.toSendMes(phoneNumber, code);
if (ComConstants.OK.equals(toSendMes)){
//redis 中存放 5分钟过期
redisTemplate.opsForValue().set(phoneNumber,code,ComConstants.NUM_FIVE, TimeUnit.MINUTES);
//3.发送短信
return "短信发送成功";
}
return "短信发送异常";
}
/**
* 检验手机验证码并登录
*/
@Override
public String checkLogin(String phoneNumber, String code) {
//1.redis 验证码校验
String redisCode = redisTemplate.opsForValue().get(phoneNumber);
if (code.equals(redisCode)){
return "登入成功";
}
return "登入失败";
}
当然,上述前端和后端代码的实现中,必然会存在一些缺陷,因为它只是一个实例
,具体的实现还需要在项目中根据需求进行完善。
3. 问题详解
3.1 常见短信服务API文档地址整理
阿里云短信服务API文档地址:https://help.aliyun.com/document_detail/101414.html
。
以下是一些其它常见短信服务供应商:
-
腾讯云短信服务API:腾讯云提供了短信服务API,用于发送短信验证码和推广短信。文档地址:
https://cloud.tencent.com/document/product/382
。 -
云片网短信API:云片网是国内的一家短信服务提供商,他们提供了简单易用的短信API接口,用于发送验证码、通知短信等。文档地址:
https://www.yunpian.com/doc/zh_CN/introduction/index.html
。 -
极光短信API:极光推送是一家提供多种推送服务的服务提供商,其中包括短信推送服务。他们提供了短信API,可以用于发送短信验证码和通知短信。文档地址:
https://docs.jiguang.cn/jpush/server/push/rest_api_v3_sms/
。
结语
建立这个平台的初衷:
-
打造一个仅包含前端问题的问答平台,让大家高效搜索处理同样问题。
-
通过不断积累问题,一起练习逻辑思维,并顺便学习相关的知识点。
-
遇到难题,遇到有共鸣的问题,一起讨论,一起沉淀,一起成长。
感谢关注微信公众号:“程序员大澈”,然后加入问答群
,让我们一起解决实现所有BUG!