package com.nova.sankuai.infra.utils; import com.nova.sankuai.domain.vo.common.MessageCode; import com.nova.sankuai.domain.vo.common.MessageImageCode; import com.nova.sankuai.domain.vo.common.MessageVerifyVo; import com.nova.sankuai.infra.config.CommonException; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import java.awt.image.BufferedImage; import java.io.IOException; import java.util.HashMap; import java.util.Map; import java.util.Random; import java.util.regex.Pattern; import static cn.hutool.core.util.ReUtil.isMatch; /** * @author weikangdi */ public class MessageCodeUtil { public static Map CODE_LIST = new HashMap<>(); public static Map IMAGE_CODE_LIST = new HashMap<>(); public static Map PHONE_VERIFY_LIST = new HashMap<>(); public static Map IP_VERIFY_LIST = new HashMap<>(); public static final int COUNT_LIMIT = 30; public static final long EXPIRED_TIME = 300000L; public static final long INTERVAL_TIME = 90000L; private static final String REGEX_MOBILE_EXACT = "^(13[0-9]|14[01456879]|15[0-35-9]|16[2567]|17[0-8]|18[0-9]|19[0-35-9])\\d{8}$"; private static final Pattern PATTERN_REGEX_MOBILE_EXACT = Pattern.compile(REGEX_MOBILE_EXACT); public static MessageCode getCode(String phone) { return CODE_LIST.get(phone); } public static void setCode(String phone, MessageCode code) { if (CODE_LIST.get(phone) == null) { CODE_LIST.put(phone, code); } else { CODE_LIST.replace(phone, code); } } public static void checkCode(String phone, String code) { MessageCode messageCode = MessageCodeUtil.getCode(phone); if (messageCode == null) { throw new CommonException("无效的验证码"); } if (messageCode.getCreateTime() != null && messageCode.getCreateTime() < (System.currentTimeMillis() - EXPIRED_TIME)) { throw new CommonException("验证码已过期"); } if (!code.equals(messageCode.getCode())) { throw new CommonException("验证码错误"); } } public static MessageCode createCode(String phone) { MessageCode messageCode = null; //随机生成验证码 String code = ""; Random random = new Random(); for (int i = 0; i < 6; i++) { int r = random.nextInt(10); code = code + r; } //生成短信验证码对象 messageCode = new MessageCode(); messageCode.setPhone(phone); messageCode.setCode(code); messageCode.setCreateTime(System.currentTimeMillis()); return messageCode; } public static MessageImageCode getImageCode(String phone) { return IMAGE_CODE_LIST.get(phone); } public static void setImageCode(String phone, MessageImageCode code) { if (IMAGE_CODE_LIST.get(phone) == null) { IMAGE_CODE_LIST.put(phone, code); } else { IMAGE_CODE_LIST.replace(phone, code); } } public static MessageVerifyVo getPhoneVerify(String phone) { return PHONE_VERIFY_LIST.get(phone); } public static void setPhoneVerify(String phone, String ip) { MessageVerifyVo messageVerifyVo = PHONE_VERIFY_LIST.get(phone); if (messageVerifyVo == null) { messageVerifyVo = new MessageVerifyVo(); messageVerifyVo.setPhone(phone); messageVerifyVo.setCount(1); messageVerifyVo.setIp(ip); messageVerifyVo.setLastApplyTime(System.currentTimeMillis()); PHONE_VERIFY_LIST.put(phone, messageVerifyVo); } else { messageVerifyVo.setCount(messageVerifyVo.getCount() + 1); messageVerifyVo.setLastApplyTime(System.currentTimeMillis()); PHONE_VERIFY_LIST.replace(phone, messageVerifyVo); } } public static MessageVerifyVo getIpVerify(String ip) { return IP_VERIFY_LIST.get(ip); } public static void setIpVerify(String phone, String ip) { MessageVerifyVo messageVerifyVo = IP_VERIFY_LIST.get(ip); if (messageVerifyVo == null) { messageVerifyVo = new MessageVerifyVo(); messageVerifyVo.setPhone(phone); messageVerifyVo.setCount(1); messageVerifyVo.setIp(ip); messageVerifyVo.setLastApplyTime(System.currentTimeMillis()); IP_VERIFY_LIST.put(ip, messageVerifyVo); } else { messageVerifyVo.setCount(messageVerifyVo.getCount() + 1); messageVerifyVo.setLastApplyTime(System.currentTimeMillis()); IP_VERIFY_LIST.replace(ip, messageVerifyVo); } } public static boolean sendVerify(String phone, String ip, Logger logger) { MessageCode code = getCode(phone); MessageVerifyVo ipVerify = getIpVerify(ip); MessageVerifyVo phoneVerify = getPhoneVerify(phone); if (phoneVerify != null) { Long lastApplyTime = phoneVerify.getLastApplyTime() + INTERVAL_TIME; if (System.currentTimeMillis() < lastApplyTime) { logger.error("登录验证码错误日志: 该手机号在验证码有效期内重复发送验证码,用户手机:" + phone + " 用户IP:" + ip); return false; } if (phoneVerify.getCount() >= COUNT_LIMIT) { logger.error("登录验证码错误日志: 该手机号已达到每日上限,用户手机:" + phone + " 用户IP:" + ip); return false; } } if (ipVerify != null) { Long lastApplyTime = ipVerify.getLastApplyTime() + INTERVAL_TIME; if (System.currentTimeMillis() < lastApplyTime) { logger.error("登录验证码错误日志: 该IP在验证码有效期内重复发送验证码,用户手机:" + phone + " 用户IP:" + ip); return false; } } if (code != null) { long time = code.getCreateTime() + INTERVAL_TIME; if (System.currentTimeMillis() < time) { logger.error("登录验证码错误日志: 验证期内重复发送验证码,用户手机:" + phone + " 用户IP:" + ip); return false; } } return true; } public static boolean verifyPhone(String phone) { if (!isMatch(PATTERN_REGEX_MOBILE_EXACT, phone)) { return false; } return true; } public static ResponseEntity getGenerateImgCode(String phone) throws IOException { ImageVerifyCode vCode = new ImageVerifyCode(); BufferedImage image = vCode.getImage(); String img = ImageVerifyCode.image2Base64(image); String verifyCode = vCode.getEncodedText(); HashMap body = new HashMap<>(); body.put("verifyCode", verifyCode); MessageCode code = MessageCodeUtil.getCode(phone); if (code == null) { body.put("isShow", false); } else { body.put("isShow", true); } body.put("img", img); MessageImageCode messageImageCode = new MessageImageCode(phone, verifyCode, System.currentTimeMillis()); setImageCode(phone, messageImageCode); ResultJson> respBody = ResultJson.ok(body); HttpHeaders headers = new HttpHeaders(); headers.set("verify-code", verifyCode); return new ResponseEntity<>(respBody, headers, HttpStatus.OK); } public static boolean verifyImageCode(String phone, String verifyCode, String inputVerifyCode) { MessageImageCode imageCode = getImageCode(phone); if (imageCode == null) { return false; } IMAGE_CODE_LIST.remove(phone); if (!imageCode.getVerifyCode().equals(verifyCode)) { return false; } if (System.currentTimeMillis() - imageCode.getCreateTime() > 3 * 60 * 1000) { // 验证码超时 return false; } if (!StringUtils.equals(verifyCode, ImageVerifyCode.encode(inputVerifyCode))) { // 图形验证码校验失败 return false; } return true; } public static void clean() { PHONE_VERIFY_LIST = new HashMap<>(); IP_VERIFY_LIST = new HashMap<>(); } }