在很多网站上都提供了短信验证码登录的功能,其思路大概是这样的:
- 前台点击发送验证码(会携带手机号)
- 调用后台接口生成验证码(3分钟有效),并将验证码存入缓存当中(这里用的是redis)
- 根据前台传送的手机号发送验证码
- 用户收到验证码之后填写验证码并登陆
- 后台收到登陆请求,先校验校正码是否有效并且正确
- 验证码校验通过之后有两种情况:
- 查询数据库,用户存在,则登录成功
- 用户不存在,跳转到注册页面
流程如下:
一、 首先创建一个SpringBoot项目,导入以下依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.3.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>org.example</groupId>
<artifactId>messageTest</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<!-- springboot启动依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!-- springboot测试依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- 合成SpringMVC功能-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- lombok插件-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!--springboot集成redis-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--腾讯云短信服务SDK-->
<dependency>
<groupId>com.github.qcloudsms</groupId>
<artifactId>qcloudsms</artifactId>
<version>1.0.2</version>
</dependency>
</dependencies>
</project>
二、查看腾讯云短信的参数配置信息,并在application.yml文件中配置
注:腾讯云短信服务是需要申请签名和模板的,如果没有申请,就去申请一个,如果有企业的话很好注册,如果是个人的话,需要先去微信公众平台 申请一个订阅号或者服务号(他们都属于微信公众号,但是我好像记得个人只能申请订阅号),申请成功之后,就可以用这个公众号去申请腾讯云的短信服务(确实挺恶心的,毕竟是一家公司)
申请成功之后:
查找自己的:
SDK AppID :******
App Key:******
TEMPLATE_ID(模版ID) :******
1、模板ID如下:
2、配置application.yml文件
server:
port: 8080
spring:
redis:
host: 127.0.0.1
port: 6379
smsConfig:
appId: 1400557427 #腾讯云短信应用id
appkey: dbae4fa0a746c533a48a7f67ebf504c7 #腾讯云短信应用key
smsSign: 华达州猛学java公众号 #腾讯云短信的签名模板
templateId: 1067012
invalidTime: 300
3、再创建一个SmsConfigProperties类读取配置文件中的值
/**
* @ClassName SmsConfig
* @Description TODO
* @Author 华达州
* @Date 2021/8/6 13:58
* @Version 1.0
**/
@Data
@Component
@ConfigurationProperties(prefix = "smsconfig")
public class SmsConfigProperties {
private int appId;
private String appKey;
private int templateId;
private String smsSign;
private int invalidTime;
}
三、集成Redis工具类
/**
* @ClassName RedisUtil
* @Description TODO
* @Author 华达州
* @Date 2021/8/6 13:54
* @Version 1.0
**/
@Component
public class RedisUtil {
@Autowired
private RedisTemplate redisTemplate;
/**
* @param key
* @return 获得值
* redis有五种数据类型 opsForValue表示是操作字符串类型
*/
public Object get(String key){
return key == null ? null : redisTemplate.opsForValue().get(key);
}
/**
* 本来只可以放入string类型,但是我们配置了自动序列化所以这儿可以传入object
* @param key
* @param value
* @return
*/
public boolean set(String key,Object value){
try{
redisTemplate.opsForValue().set(key,value);
return true;
}catch (Exception e){
return false;
}
}
/**
* 原子操作
* @param key
* @param value
* @param expire 过期时间 秒
* @return
*/
public boolean setex(String key,Object value,long expire){
try{
//TimeUnit.SECONDS指定类型为秒
redisTemplate.opsForValue().set(key,value,expire, TimeUnit.SECONDS);
return true;
}catch (Exception e){
return false;
}
}
/**
* 非原子操作
* @param key
* @param expire
* @return
*/
public boolean expire(String key,long expire){
try{
//这儿没有ops什么的是因为每种数据类型都能设置过期时间
redisTemplate.expire(key,expire,TimeUnit.SECONDS);
return true;
}catch (Exception e){
return false;
}
}
/**
*
* @param key
* @return 获取key的过期时间
*/
public long ttl(String key){
return redisTemplate.getExpire(key);
}
/**
*
* @param keys 删除key 可变参数
*/
public void del(String ...keys){
if(keys!=null&&keys.length>0) {
redisTemplate.delete((Collection<String>) CollectionUtils.arrayToList(keys));
}
}
/**
*
* @param key
* @param step 传入正数 就是加多少 传入负数就是减多少
* @return
*/
public long incrBy(String key,long step){
return redisTemplate.opsForValue().increment(key,step);
}
/**
*
* @param key
* @param value
* @return 如果该key存在就返回false 设置不成功 key不存在就返回ture设置成功
*/
public boolean setnx(String key,Object value){
return redisTemplate.opsForValue().setIfAbsent(key,value);
}
/**
* 原子操作
* @param key
* @param value
* @param expire 在上面方法加上过期时间设置
* @return
*/
public boolean setnxAndExpire(String key,Object value,long expire){
return redisTemplate.opsForValue().setIfAbsent(key,value,expire,TimeUnit.SECONDS);
}
/**
*
* @param key
* @param value
* @return 如果该key存在就返回之前的value 不存在就返回null
*/
public Object getAndSet(String key,Object value){
return redisTemplate.opsForValue().getAndSet(key,value);
}
/**
*
* @param key
* @return 判断key是否存在
*/
public boolean hasKey(String key){
return redisTemplate.hasKey(key);
}
四、写Controller接口
@Controller
public class SendMsgController {
@Autowired
private UserService userService;
@Autowired
private SendSmsUtil sendSmsUtil;
@Autowired
private RedisUtil redisUtil;
/**
* 发送验证码
* @author 华达州
* @date 2021/8/6 21:35
* @param phone
* @return java.lang.String
*/
@PostMapping("sendCode")
@ResponseBody
public String sendCode(@RequestParam String phone){
return sendSmsUtil.sendSms(phone);
}
/**
* 使用手机号验证码登录
* @author 华达州
* @date 2021/8/6 20:26
* @return java.lang.String
*/
@PostMapping("codeLogin") //腾讯云API推荐使用post请求
@ResponseBody
public String codeLogin(@RequestParam("phone") String phone,@RequestParam("code")String code){
User user = userService.getOne(new QueryWrapper<User>().lambda().eq(User::getPhone, phone));
if(user==null){
return "用户不存在";
}
String oldCode = (String) redisUtil.get(phone);
if(!code.equals(oldCode)){
return "验证码错误";
}
return "登录成功";
}
}
上面有两个接口
第一个接口是前端页面点击发送验证码访问的接口,之后用户就会在手机端收到短信验证码
第二个接口是填写验证码之后点击登录访问的接口,访问逻辑和我们开头说的一样,这里不再赘述
这里我就不写前端页面进行测试了
五、直接使用postman进行测试
1、先测试发送验证码
之后在手机端收到如下短信
2、接着测试登录接口(五分钟之内)
可以看到返回登录成功的消息
注意:这里返回登录需要在后台保存了该用户的信息,否则会返回用户不存在,这个就留着你们自己测试吧!!