首先图片验证码已经在生成的时候就存入redis了
图形验证码代码如下:
/**
* 生成验证码
* @param request
* @param response
*/
@GetMapping("/captcha")
public void getCaptcha(HttpServletRequest request, HttpServletResponse response){
String capcheaText = capcheaProducer.createText();
log.info("验证码内容为:{}",capcheaText);
//存储到redis中,配置过期时间 TODO
redisTemplate.opsForValue().set(getCaptchaKey(request),capcheaText, CAPTCHA_KEY_TIMEOUT, TimeUnit.MILLISECONDS);
BufferedImage bufferedImage = capcheaProducer.createImage(capcheaText);
try(ServletOutputStream outputStream = response.getOutputStream();) {
ImageIO.write(bufferedImage,"jpg",outputStream);
outputStream.flush();
} catch (IOException e) {
log.error("获取流出错:{}",e.getMessage());
}
}
private String getCaptchaKey(HttpServletRequest request) {
String ip = CommonUtil.getIpAddr(request);
String userAgent = request.getHeader("User-Agent");
String captchaKey = "account-service:captcha:" + CommonUtil.MD5(ip + userAgent);
log.info("验证码key是:{}",captchaKey);
return captchaKey;
}
短信验证码接口实现
需求:一定时间内禁止重复发送验证码
方式一:前端设置校验倒计时按钮,不到60s按钮不准点击(不安全,可以直接请求接口,否决)
方式二:增加redis存储,发送的时候设置额外的key,并且60s后过期(非原子操作,存在不一致,增加额外的key,value存储,浪费空间)
方式三:基于原先的key拼装时间戳 好处 满足了当前节点内的原子性,也满足业务需求
两个时间要求:
60s后才可以重新发送短信验证码(存入redis的值格式为:"验证码_"+时间戳)
发送的短信验证码10分钟内有效
具体实现代码如下:
@Override
public JsonData sendSMS(SendCodeEnum sendCodeEnum , String to) {
String captchaKey = String.format(RedisKey.CHECK_CODE_KEY, sendCodeEnum.name(), to);
String captchavalue = stringRedisTemplate.opsForValue().get(captchaKey);
//如果值不为空,在判断是否是60s重复发送
if (StringUtils.isNotBlank(captchavalue)){
long ttl = Long.parseLong(captchavalue.split("_")[1]);
//当前时间戳-验证码发送的时间戳,如果小于60s,则不给重复发送]
long leftTime = CommonUtil.getCurrentTimestamp() - ttl;
if( leftTime< (1000*60)){
log.info("重复发送短信验证码,时间间隔:{}s",leftTime);
return JsonData.buildSuccess(BizCodeEnum.CODE_LIMITED);
}
}
String code = CommonUtil.getRandomCode(6);
//生成拼接验证码
String value = code+"_"+CommonUtil.getCurrentTimestamp();
stringRedisTemplate.opsForValue().set(captchaKey,value,CODE_EXPIRED, TimeUnit.MILLISECONDS);
if (CheckUtil.isEmail(to)){
//发送邮箱验证码
}else if (CheckUtil.isPhone(to)){
//发送手机号验证码
smsComponent.send(to,smsConfig.getTemplateId(),code);
}
return JsonData.buildSuccess();
}
踩坑:会报错两个位置
解决问题:手机校验规则得更新 发送短信验证码连接请用请求http协议
校验规则代码如下:
package net.xdclass.util;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @Author tt
* @Date 2024/4/2 19:51
* @PackageName:net.xdclass.util
* @ClassName: CheckUtil
* @Description: TODO
*/
public class CheckUtil {
/**
* 邮箱
*/
private static final Pattern MAIL_PATTERN = Pattern.compile("^([a-z0-9A-Z]+[-|\\.]?)+[a-z0-9A-Z]@([a-z0-9A-Z]+(-[a-z0-9A-Z]+)?\\.)+[a-zA-Z]{2,}$");
private static final Pattern PHONE_PATTERN = Pattern.compile("^((13[0-9])|(14(0|[5-7]|9))|(15([0-3]|[5-9]))|(16(2|[5-7]))|(17[0-8])|(18[0-9])|(19([0-3]|[5-9])))\\d{8}$");
public static boolean isEmail(String email) {
if (null == email || "".equals(email)) {
return false;
}
Matcher m = MAIL_PATTERN.matcher(email);
return m.matches();
}
public static boolean isPhone(String phone){
if (phone ==null || "".equals(phone)){
return false;
}
Matcher m = PHONE_PATTERN.matcher(phone);
return m.matches();
}
}
SmsComponent组件中修改url
private static final String URL_TEMPLATE = "http://jmsms.market.alicloudapi.com/sms/send?mobile=%s&templateId=%s&value=%s";