前言:本文章不介绍发送短信具体流程,仅仅记录下如何在后端限制一个手机号一分钟内只能一次发送验证码,避免有人恶意一直重复获取验证码。
一、正常情况下的获取验证码及校验验证码
一般情况下获取验证码流程:前端传递手机号到后端,后端校验手机号后生成四位随机数验证码调用发送验证码的接口发送验证码(由于我这里使用的是平台封装好的接口,就不具体阐述发送流程),其验证码信息只在申请提交时使用存入数据库显然比浪费资源,所以我们存入redis内,并生成随机数,随机数作为key,验证码作为value,组成一个key-value,并设置过期时间。将随机数key返回给前端。
代码如下:
public String getAuthCode(String mobile){
try {
String s = null;
if (StringUtils.isNotEmpty(mobile)){
int authCodeNew = (int)Math.round(Math.random() * 8999.0D + 1000.0D);
//获取验证码
Integer authCode = SendSmsUtil.sendSmsToUserByCustomContent(mobile,Integer.toString(authCodeNew));
s = UUID.randomUUID().toString();
//存入redis
redisTemplate.opsForValue().set(s,authCodeNew,3, TimeUnit.MINUTES);
}
return s;
} catch (Exception e) {
logger.error("请求失败,请检查网络或重试!");
e.printStackTrace();
throw new ServiceException("请求失败,请检查网络或重试!", CommonErrorCode.EXCEPTION_ERROR);
}
}
存入之后当前端提交申请之后我们就需要进行验证码校验,看了上面验证码发送过程大家其实已经知道了如何进行验证码校验,就是通过上一步返回到前端的key和收到的验证码,作为参数传递到后端,使用key在redis进行查询与验证码进行比较,废话不多说上代码:
@Override
public boolean checkAuthCode(String key,String authCode){
try {
if(StringUtils.isNotEmpty(key)){
Integer code = (Integer)redisTemplate.opsForValue().get(key);
if (null != code && String.valueOf(code).equals(authCode)){
return true;
}else {
return false;
}
}else {
return false;
}
} catch (Exception e) {
logger.error("请求失败,请检查网络或重试!");
e.printStackTrace();
throw new ServiceException("请求失败,请检查网络或重试!", CommonErrorCode.EXCEPTION_ERROR);
}
}
二、限制一分钟内一个手机号只能发送一次验证码
了解过发送验证码平台的应该知道,像阿里云这种一般都会对请求次数进行限制短时间内超过最大限制便会不在发送验证码,但那个要求在短时间内大量请求,在我们后端是否可以先自己进行拦截呢?平台发送验证码也要收费的,限制后这样既避免了恶意请求也给我们节省了费用。在前端我也做了限制,点击后一分钟内无法点击发送按钮,但是如果用户刷新页面则会导致按钮计时重置,还是可以继续发送。
下面先说说我的思路吧:判断是否该手机号发送过验证码我们的目标,最初我想法是能否通过手机号查看到是否生成过验证码,判断验证码是否过期,但我并未对手机号进行存储,我便想到了在存储验证码的同时将手机号当做key存入redis,下次请求时先去redis查询手机号的key,若存在则返回固定提示,不存在正常发送验证码。只需要对发送验证码的方法做改变。(返回前端的数据若发送了验证码是随机的key,若没发送是固定的字符串,前端只需要对固定的字符串做判断后进行提示即可)
public String getAuthCode(String mobile){
try {
String s = null;
String str = null;
if (StringUtils.isNotEmpty(mobile)){
str = (String)redisTemplate.opsForValue().get(mobile);
if("repeated".equals(str)){
return str;
}
int authCodeNew = (int)Math.round(Math.random() * 8999.0D + 1000.0D);
Integer authCode = SendSmsUtil.sendSmsToUserByCustomContent(mobile,Integer.toString(authCodeNew));
s = UUID.randomUUID().toString();
redisTemplate.opsForValue().set(s,authCodeNew,3, TimeUnit.MINUTES);
redisTemplate.opsForValue().set(mobile,"repeated",1, TimeUnit.MINUTES);
}
return s;
} catch (Exception e) {
logger.error("请求失败,请检查网络或重试!");
e.printStackTrace();
throw new ServiceException("请求失败,请检查网络或重试!", CommonErrorCode.EXCEPTION_ERROR);
}
}