目录
这篇文章之前,看一下qq验证码文章:http://t.csdnimg.cn/7hnpz
1.邮箱注册controller层
- 1.根据ip解析,获取key
- 2.根据key从redis中获取验证码
- 3.判断前端验证码是否与redis中的一致
- 4.删除key
- 5.给邮箱发送注册验证码
/**
* redis
*/
@Autowired
private StringRedisTemplate redisTemplate;
@Autowired
private NotifyService notifyService;
@ApiOperation("发送邮箱注册验证码")
@GetMapping("/send_code")
public JsonData sendRegisterCode(@RequestParam(value = "to", required = true) String to,
@RequestParam(value = "captcha", required = true) String captcha,
HttpServletRequest request) {
//1.根据浏览器获取key
String key = getCaptchaKey(request);
//2.根据key从浏览器获取验证码
String captchaKey = redisTemplate.opsForValue().get(key);
//3.判断前端返回的验证码是否与redis中一致
if (captcha != null && captchaKey != null && captchaKey.equalsIgnoreCase(captcha)) {
//相等
//删除key
redisTemplate.delete(key);
//注册
JsonData jsonData = notifyService.sendCode(SendCodeEnum.USER_REGISTER, to);
log.info("发送成功");
return JsonData.buildSuccess(jsonData);
} else {
//不相等
return JsonData.buildResult(CodeEnum.CODE_CAPTCHA_ERROR);
}
}
2.service实现类
- 1.判断邮箱或手机号是否合规
- 2.根据工具类,指定随机验证码的位数
- 3.调用邮箱发送接口,发送验证码
@Slf4j
@Service
public class NotifyServiceImpl implements NotifyService {
@Autowired
private MailService mailService;
private static final String SUBJECT = "欢迎查收小张的邮箱~";
private static final String CONTENT = "你的验证码%s,有效时间60秒,悄悄地哦";
/**
* 发送验证码
*
* @param sendCodeEnum:发送类型
* @param to:收件人邮箱
* @return
*/
@Override
public JsonData sendCode(SendCodeEnum sendCodeEnum, String to) {
//1.判断是否合规
if (CheckUtil.isEmail(to)) {
//指定邮箱验证的位数
String randomCode = CommonUtil.getRandomCode(6);
//发送随机验证码
mailService.sendMail(to, SUBJECT, String.format(CONTENT, randomCode));
return JsonData.buildSuccess("发送随机验证成功~");
} else {
return JsonData.buildResult(CodeEnum.CODE_TO_ERROR);
}
}
}
3.工具类
校验邮箱电话号码是否合规,以及获取ip、md5加密、获取随机验证码
/**
* 工具类,校验
*/
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])|(15[^4,\\D])|(18[0,5-9]))\\d{8}$");
/**
* 邮箱校验
*
* @param email
* @return
*/
public static boolean isEmail(String email) {
if (null == email || "".equals(email)) {
return false;
}
Matcher m = MAIL_PATTERN.matcher(email);
return m.matches();
}
/**
* 手机校验
*
* @param phone
* @return
*/
public static boolean isPhone(String phone) {
if (null == phone || "".equals(phone)) {
return false;
}
Matcher m = PHONE_PATTERN.matcher(phone);
return m.matches();
}
/**
* 获取ip
*
* @param request
* @return
*/
public static String getIpAddr(HttpServletRequest request) {
String ipAddress = null;
try {
ipAddress = request.getHeader("x-forwarded-for");
if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getHeader("Proxy-Client-IP");
}
if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getHeader("WL-Proxy-Client-IP");
}
if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getRemoteAddr();
if (ipAddress.equals("127.0.0.1")) {
// 根据网卡取本机配置的IP
InetAddress inet = null;
try {
inet = InetAddress.getLocalHost();
} catch (UnknownHostException e) {
e.printStackTrace();
}
ipAddress = inet.getHostAddress();
}
}
// 对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割
if (ipAddress != null && ipAddress.length() > 15) {
// "***.***.***.***".length()
// = 15
if (ipAddress.indexOf(",") > 0) {
ipAddress = ipAddress.substring(0, ipAddress.indexOf(","));
}
}
} catch (Exception e) {
ipAddress = "";
}
return ipAddress;
}
/**
* MD5加密
*
* @param data
* @return
*/
public static String MD5(String data) {
try {
java.security.MessageDigest md = MessageDigest.getInstance("MD5");
byte[] array = md.digest(data.getBytes("UTF-8"));
StringBuilder sb = new StringBuilder();
for (byte item : array) {
sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
}
return sb.toString().toUpperCase();
} catch (Exception exception) {
}
return null;
}
/**
* 获取验证码随机数
*
* @param length
* @return
*/
public static String getRandomCode(int length) {
String source = "0123456789";
Random random = new Random();
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < length; i++) {
stringBuilder.append(source.charAt(random.nextInt(9)));
}
return stringBuilder.toString();
}
/**
* 获取当前时间戳
*
* @return
*/
public static Long getCurrentTimestamp() {
return System.currentTimeMillis();
}
}
4.验证码防刷方案
- 1.前端增加校验倒计时,不到60秒按钮不给点击
- 2.增加Redis存储,发送的时候设置下额外的key,并且60秒后过期
- 3.基于原先的key拼装时间戳
方案二:使用redis缓存,防刷验证码
- 1.根据发送人和类型,获取缓存key
- 2.根绝获取到的缓存key,从redis中获取value
- 3.判断value是否为空,
- 如果不为空,利用 _ 分割获取的value,拿到时间戳,当前时间戳-获取到的如果小于60s则不让其发送
- 如果为空,则将获取到的验证码拼接时间戳作为value,设置过期时间存储到缓存redis中
/**
* 发送验证码
*
* @param sendCodeEnum:发送类型
* @param to:收件人邮箱
* @return
*/
@Override
public JsonData sendCode(SendCodeEnum sendCodeEnum, String to) {
//获取缓存key
String cacheKey = String.format(CaptchaKey.CAPTCHA_CODE_KEY, sendCodeEnum.name(), to);
//根据key获取value
String cacheValue = redisTemplate.opsForValue().get(cacheKey);
//如果不为空,则判断60秒内重复发送
if (StringUtils.isNotBlank(cacheValue)) {
long ttl = Long.parseLong(cacheValue.split("_")[1]);
//当前时间戳-验证码发送时间错,如果小于60,不重复发送
if (CommonUtil.getCurrentTimestamp() - ttl < 1000 * 60){
log.info("重复发送验证码,时间间隔:{}秒",(CommonUtil.getCurrentTimestamp() - ttl)/1000);
return JsonData.buildResult(CodeEnum.CODE_LIMITED);
}
}
//获取随机验证码
String randomCode = CommonUtil.getRandomCode(6);
//获取到的验证码拼接上时间戳
String value = randomCode + "_" + CommonUtil.getCurrentTimestamp();
//将拼接后的验证码存到redis中
redisTemplate.opsForValue().set(cacheKey, value, CODE_EXPIRED, TimeUnit.MILLISECONDS);
//1.判断是否合规
if (CheckUtil.isEmail(to)) {
//指定邮箱验证的位数
//发送随机验证码
mailService.sendMail(to, SUBJECT, String.format(CONTENT, randomCode));
return JsonData.buildSuccess("发送随机验证成功~");
} else {
return JsonData.buildResult(CodeEnum.CODE_TO_ERROR);
}
}
}