Commit 8a797d43 authored by Java—红包—徐 然's avatar Java—红包—徐 然

Merge branch 'master' into feature/hongbao

parents d65b6eed 7bcedcf8
...@@ -25,8 +25,18 @@ public interface Constants { ...@@ -25,8 +25,18 @@ public interface Constants {
String REDIS_PREFIX_VERIFICATION_CODE = "verificationCode_"; String REDIS_PREFIX_VERIFICATION_CODE = "verificationCode_";
String REDIS_VOICE_CODE_COUNT = "voice_verification_code_count:"; String REDIS_PREFIX_VERIFICATION_VOICE_CODE = "verificationCode_voice_";
//新版短信验证码计数
String REDIS_SMS_CODE_COUNT = "SMS_Phone_verification_code_count:";
String REDIS_SMS_IP_COUNT = "SMS_Ip_verification_code_count:";
String REDIS_SMS_DEVICE_COUNT = "SMS_Device_verification_code_count:";
//新版语音验证码计数
String REDIS_VOICE_CODE_COUNT = "Voice_Phone_verification_code_count:";
String REDIS_VOICE_IP_COUNT = "Voice_Ip_verification_code_count:";
String REDIS_VOICE_DEVICE_COUNT = "Voice_Device_verification_code_count:";
String REDIS_VERIFICATION_COUNT = "verification_code_count:";
final Long Image_Need_Count=3L;
/** /**
* redis中token的key值前缀 * redis中token的key值前缀
*/ */
......
package cn.quantgroup.xyqb.aspect.captcha;
import cn.quantgroup.xyqb.Constants;
import cn.quantgroup.xyqb.controller.IBaseController;
import cn.quantgroup.xyqb.model.JsonResult;
import cn.quantgroup.xyqb.thirdparty.jcaptcha.AbstractManageableImageCaptchaService;
import com.octo.captcha.service.CaptchaServiceException;
import java.io.PipedReader;
import java.nio.charset.Charset;
import java.security.PrivateKey;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
/**
* 类名称:CaptchaValidateAdvisor
* 类描述:
*
* @author 李宁
* @version 1.0.0 创建时间:15/11/17 14:49 修改人: 修改时间:15/11/17 14:49 修改备注:
*/
@Aspect
@Component
public class CaptchaNewValidateAdvisor {
private static final Logger LOGGER = LoggerFactory.getLogger(CaptchaNewValidateAdvisor.class);
private static final String SUPER_CAPTCHA_ID = UUID.nameUUIDFromBytes("__QG_APPCLIENT_AGENT__".getBytes(Charset.forName("UTF-8"))).toString();
private static final String SUPER_CAPTCHA = "__SUPERQG__";
@Autowired
@Qualifier("stringRedisTemplate")
private RedisTemplate<String, String> redisTemplate;
@Autowired
@Qualifier("customCaptchaService")
private AbstractManageableImageCaptchaService imageCaptchaService;
/**
* 自动化测试忽略验证码
*/
@Value("${xyqb.auth.captcha.autotest.enable:false}")
private boolean autoTestCaptchaEnabled;
/**
* 图形验证码切面
*/
@Pointcut("@annotation(cn.quantgroup.xyqb.aspect.captcha.CaptchaNewValidator)")
private void needNewCaptchaValidate() {
}
private static final String IMAGE_IP_COUNT = "image:ip";
private static final String IMAGE_PHONE_COUNT = "image:phone";
private static final String IMAGE_DEVICEID_COUNT = "image:deviceId:";
private static final Long FIVE_MIN = 24 * 5L;
/**
* 在受图形验证码保护的接口方法执行前, 执行图形验证码校验
*
* @throws Throwable
*/
@Around("needNewCaptchaValidate()")
private Object doCapchaValidate(ProceedingJoinPoint pjp) throws Throwable {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
String registerFrom = Optional.ofNullable(request.getParameter("registerFrom")).orElse("");
String captchaId = Optional.ofNullable(request.getParameter("captchaId")).orElse("");
Object captchaValue = request.getParameter("captchaValue");
String phoneNo = request.getParameter("phoneNo");
String deviceId = Optional.ofNullable(request.getParameter("deviceId")).orElse("");
String clientIp = getIp();
Long countIP = countIP(clientIp);
Long countPhone = countPhone(phoneNo);
Long countDeviceId = countDeviceId(deviceId);
if (countIP > Constants.Image_Need_Count || countPhone > Constants.Image_Need_Count || countDeviceId > Constants.Image_Need_Count) {
if (shouldSkipCaptchaValidate(registerFrom, captchaId, captchaValue)) {
LOGGER.info("使用超级图形验证码校验, registerFrom={}, clientIp={}", registerFrom, request.getRemoteAddr());
return pjp.proceed();
}
JsonResult result = JsonResult.buildSuccessResult("图形验证码不正确", "");
result.setBusinessCode("0002");
if (captchaValue != null) {
String captcha = String.valueOf(captchaValue);
// 忽略用户输入的大小写
captcha = StringUtils.lowerCase(captcha);
// 验证码校验
Boolean validCaptcha = false;
try {
validCaptcha = imageCaptchaService.validateResponseForID(Constants.IMAGE_CAPTCHA_KEY + captchaId, captcha);
} catch (CaptchaServiceException ex) {
LOGGER.error("验证码校验异常, {}, {}", ex.getMessage(), ex);
}
if (validCaptcha) {
return pjp.proceed();
}
}
return result;
}
return pjp.proceed();
}
private boolean shouldSkipCaptchaValidate(String registerFrom, String captchaId, Object captchaValue) {
// 如果启用了超级验证码功能, 检查超级验证码, 超级验证码区分大小写
if (autoTestCaptchaEnabled) {
return true;
}
return StringUtils.equals(SUPER_CAPTCHA_ID, String.valueOf(captchaId)) && StringUtils.equals(SUPER_CAPTCHA, String.valueOf(captchaValue));
}
private Long countIP(String clientIp) {
Long count = 1L;
if (StringUtils.isBlank(clientIp)) {
return count;
} else {
String countString = redisTemplate.opsForValue().get(IMAGE_IP_COUNT + clientIp);
if (StringUtils.isBlank(countString)) {
redisTemplate.opsForValue().set(IMAGE_IP_COUNT + clientIp, String.valueOf(count),
FIVE_MIN, TimeUnit.SECONDS);
} else {
count = Long.valueOf(countString) + 1L;
redisTemplate.opsForValue().set(IMAGE_IP_COUNT + clientIp, String.valueOf(count),
FIVE_MIN, TimeUnit.SECONDS);
}
return count;
}
}
private Long countPhone(String phoneNo) {
Long count = 1L;
String countString = redisTemplate.opsForValue().get(IMAGE_PHONE_COUNT + phoneNo);
if (StringUtils.isBlank(countString)) {
redisTemplate.opsForValue().set(IMAGE_PHONE_COUNT + phoneNo, String.valueOf(count),
FIVE_MIN, TimeUnit.SECONDS);
} else {
count = Long.valueOf(countString) + 1L;
redisTemplate.opsForValue().set(IMAGE_PHONE_COUNT + phoneNo, String.valueOf(count),
FIVE_MIN, TimeUnit.SECONDS);
}
return count;
}
/**
* 短信发送设备限制
*/
private Long countDeviceId(String deviceId) {
Long count = 1L;
if (StringUtils.isBlank(deviceId)) {
return count;
} else {
String countString = redisTemplate.opsForValue().get(IMAGE_DEVICEID_COUNT + deviceId);
if (StringUtils.isBlank(countString)) {
redisTemplate.opsForValue().set(IMAGE_DEVICEID_COUNT + deviceId, String.valueOf(count),
FIVE_MIN, TimeUnit.SECONDS);
} else {
count = Long.valueOf(countString) + 1L;
redisTemplate.opsForValue().set(IMAGE_DEVICEID_COUNT + deviceId, String.valueOf(count),
FIVE_MIN, TimeUnit.SECONDS);
}
return count;
}
}
private String getIp() {
HttpServletRequest request = getRequest();
String ip = request.getHeader("x-real-ip");
if (StringUtils.isEmpty(ip)) {
ip = request.getRemoteAddr();
}
//过滤反向代理的ip
String[] stemps = ip.split(",");
if (stemps.length >= 1) {
//得到第一个IP,即客户端真实IP
ip = stemps[0];
}
ip = ip.trim();
if (ip.length() > 23) {
ip = ip.substring(0, 23);
}
return ip;
}
private HttpServletRequest getRequest() {
ServletRequestAttributes attrs = (ServletRequestAttributes) RequestContextHolder
.getRequestAttributes();
return attrs.getRequest();
}
}
package cn.quantgroup.xyqb.aspect.captcha;
import java.lang.annotation.*;
/**
* Created by xuran on 2017/8/28.
*/
@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CaptchaNewValidator {
}
...@@ -85,7 +85,7 @@ public class CaptchaValidateAdvisor { ...@@ -85,7 +85,7 @@ public class CaptchaValidateAdvisor {
return pjp.proceed(); return pjp.proceed();
} }
JsonResult result = JsonResult.buildSuccessResult("图形验证码错误, 请重新输入", ""); JsonResult result = JsonResult.buildSuccessResult("图形验证码不正确", "");
result.setBusinessCode("0002"); result.setBusinessCode("0002");
if (captchaValue != null) { if (captchaValue != null) {
String captcha = String.valueOf(captchaValue); String captcha = String.valueOf(captchaValue);
......
...@@ -5,13 +5,21 @@ import cn.quantgroup.xyqb.aspect.captcha.CaptchaValidator; ...@@ -5,13 +5,21 @@ import cn.quantgroup.xyqb.aspect.captcha.CaptchaValidator;
import cn.quantgroup.xyqb.controller.IBaseController; import cn.quantgroup.xyqb.controller.IBaseController;
import cn.quantgroup.xyqb.model.JsonResult; import cn.quantgroup.xyqb.model.JsonResult;
import cn.quantgroup.xyqb.thirdparty.jcaptcha.AbstractManageableImageCaptchaService; import cn.quantgroup.xyqb.thirdparty.jcaptcha.AbstractManageableImageCaptchaService;
import com.octo.captcha.service.CaptchaServiceException;
import java.nio.charset.Charset;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import org.apache.commons.codec.binary.Base64; import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import javax.imageio.ImageIO; import javax.imageio.ImageIO;
...@@ -22,41 +30,49 @@ import java.io.IOException; ...@@ -22,41 +30,49 @@ import java.io.IOException;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.UUID; import java.util.UUID;
import sun.management.counter.LongCounter;
/** /**
* 类名称:ImgCaptchaController * 类名称:ImgCaptchaController
* 类描述:图形验证码控制器 * 类描述:图形验证码控制器
* *
* @author 李宁 * @author 李宁
* @version 1.0.0 * @version 1.0.0 创建时间:15/11/17 11:49 修改人: 修改时间:15/11/17 11:49 修改备注:
* 创建时间:15/11/17 11:49
* 修改人:
* 修改时间:15/11/17 11:49
* 修改备注:
*/ */
@RestController @RestController
@RequestMapping("/api") @RequestMapping("/api")
public class ImageCaptchaController implements IBaseController { public class ImageCaptchaController implements IBaseController {
private static final Logger LOGGER = LoggerFactory.getLogger(ImageCaptchaController.class); private static final Logger LOGGER = LoggerFactory.getLogger(ImageCaptchaController.class);
private static final String SUPER_CAPTCHA_ID = UUID.nameUUIDFromBytes("__QG_APPCLIENT_AGENT__".getBytes(Charset.forName("UTF-8"))).toString();
private static final String SUPER_CAPTCHA = "__SUPERQG__";
private static final String IMAGE_FORMAT_PNG = "png"; private static final String IMAGE_FORMAT_PNG = "png";
private static final String IMG_BASE64_PATTREN = "data:image/" + IMAGE_FORMAT_PNG + ";base64,%s"; private static final String IMG_BASE64_PATTREN = "data:image/" + IMAGE_FORMAT_PNG + ";base64,%s";
private static final String IMAGE_IP_COUNT = "image:ip";
private static final String IMAGE_PHONE_COUNT = "image:phone";
private static final Long FIVE_MIN = 24 * 5L;
@Autowired @Autowired
@Qualifier("customCaptchaService") @Qualifier("customCaptchaService")
private AbstractManageableImageCaptchaService imageCaptchaService; private AbstractManageableImageCaptchaService imageCaptchaService;
@Autowired
@Qualifier("stringRedisTemplate")
private RedisTemplate<String, String> redisTemplate;
@ModelAttribute("clientIp") @ModelAttribute("clientIp")
public String initClientIp() { public String initClientIp() {
return getIp(); return getIp();
} }
/**
* 自动化测试忽略验证码
*/
@Value("${xyqb.auth.captcha.autotest.enable:false}")
private boolean autoTestCaptchaEnabled;
/** /**
* 获取验证码 * 获取验证码
* 默认匹配 GET /captcha, 提供4位数字和字母混合图片验证码 * 默认匹配 GET /captcha, 提供4位数字和字母混合图片验证码
*
* @return
*/ */
@RequestMapping(value = "/captcha") @RequestMapping(value = "/captcha")
public JsonResult fetchCaptcha(HttpServletRequest request, @ModelAttribute("clientIp") String clientIp) { public JsonResult fetchCaptcha(HttpServletRequest request, @ModelAttribute("clientIp") String clientIp) {
...@@ -76,9 +92,7 @@ public class ImageCaptchaController implements IBaseController { ...@@ -76,9 +92,7 @@ public class ImageCaptchaController implements IBaseController {
data.put("imageId", imageId); data.put("imageId", imageId);
data.put("image", String.format(IMG_BASE64_PATTREN, imageBase64)); data.put("image", String.format(IMG_BASE64_PATTREN, imageBase64));
return JsonResult.buildSuccessResult("", data); return JsonResult.buildSuccessResult("", data);
} }
/** /**
* 图片验证码验证 * 图片验证码验证
*/ */
...@@ -87,5 +101,4 @@ public class ImageCaptchaController implements IBaseController { ...@@ -87,5 +101,4 @@ public class ImageCaptchaController implements IBaseController {
public JsonResult verificationImageCode() { public JsonResult verificationImageCode() {
return JsonResult.buildSuccessResult("", null); return JsonResult.buildSuccessResult("", null);
} }
} }
...@@ -2,7 +2,9 @@ package cn.quantgroup.xyqb.controller.internal.sms; ...@@ -2,7 +2,9 @@ package cn.quantgroup.xyqb.controller.internal.sms;
import cn.quantgroup.sms.MsgParams; import cn.quantgroup.sms.MsgParams;
import cn.quantgroup.xyqb.Constants; import cn.quantgroup.xyqb.Constants;
import cn.quantgroup.xyqb.aspect.captcha.CaptchaNewValidator;
import cn.quantgroup.xyqb.aspect.captcha.CaptchaValidator; import cn.quantgroup.xyqb.aspect.captcha.CaptchaValidator;
import cn.quantgroup.xyqb.controller.IBaseController;
import cn.quantgroup.xyqb.model.JsonResult; import cn.quantgroup.xyqb.model.JsonResult;
import cn.quantgroup.xyqb.service.sms.ISmsService; import cn.quantgroup.xyqb.service.sms.ISmsService;
import cn.quantgroup.xyqb.util.DateUtils; import cn.quantgroup.xyqb.util.DateUtils;
...@@ -14,6 +16,7 @@ import org.springframework.beans.factory.annotation.Autowired; ...@@ -14,6 +16,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
...@@ -26,7 +29,7 @@ import java.util.concurrent.TimeUnit; ...@@ -26,7 +29,7 @@ import java.util.concurrent.TimeUnit;
*/ */
@RestController @RestController
@RequestMapping("/api/sms") @RequestMapping("/api/sms")
public class SmsController { public class SmsController implements IBaseController {
private static final Logger LOGGER = LoggerFactory.getLogger(SmsController.class); private static final Logger LOGGER = LoggerFactory.getLogger(SmsController.class);
private static final Random random = new Random(); private static final Random random = new Random();
...@@ -38,7 +41,14 @@ public class SmsController { ...@@ -38,7 +41,14 @@ public class SmsController {
private RedisTemplate<String, String> redisTemplate; private RedisTemplate<String, String> redisTemplate;
@Value("${sms.is.debug}") @Value("${sms.is.debug}")
private boolean smsIsDebug; private boolean smsIsDebug;
private static final String IMAGE_IP_COUNT = "image:ip";
private static final String IMAGE_PHONE_COUNT = "image:phone";
private static final String IMAGE_DEVICEID_COUNT = "image:deviceId:";
private static final Long IP_MAX_PER_DAY = 5000L;//ip上限
private static final Long PHONE_MAX_PER_DAY = 20L;//手机号短信上限
private static final Long PHONE_VOICE_MAX_PER_DAY = 5L;//手机号语音上限
private static final Long DEVICE_MAX_PER_DAY = 20L;//设备每天上限
/** /**
* 短信验证码: for H5 * 短信验证码: for H5
* 使用 @FPLock 注解并加入自定义限制参数, 做针对手机号的发送次数限制 * 使用 @FPLock 注解并加入自定义限制参数, 做针对手机号的发送次数限制
...@@ -142,6 +152,7 @@ public class SmsController { ...@@ -142,6 +152,7 @@ public class SmsController {
//smsService.getSmsSender().sendConfirmableMessage(confirmableMsg); //smsService.getSmsSender().sendConfirmableMessage(confirmableMsg);
smsService.getSmsSender().sendMsg(message); smsService.getSmsSender().sendMsg(message);
redisTemplate.opsForValue().set(key, uniqueId + ":" + randomCode, EXPIRE_MINUTES, TimeUnit.MINUTES); redisTemplate.opsForValue().set(key, uniqueId + ":" + randomCode, EXPIRE_MINUTES, TimeUnit.MINUTES);
deleteRetSendCode(phoneNo);//删除用户重置密码,多次错误逻辑
return JsonResult.buildSuccessResult("发送成功", uniqueId); return JsonResult.buildSuccessResult("发送成功", uniqueId);
} catch (Exception e) { } catch (Exception e) {
LOGGER.error("发送短信验证码失败"); LOGGER.error("发送短信验证码失败");
...@@ -170,10 +181,179 @@ public class SmsController { ...@@ -170,10 +181,179 @@ public class SmsController {
try { try {
smsService.getSmsSender().sendMsg(message); smsService.getSmsSender().sendMsg(message);
redisTemplate.opsForValue().set(key, uniqueId + ":" + randomCode, EXPIRE_MINUTES, TimeUnit.MINUTES); redisTemplate.opsForValue().set(key, uniqueId + ":" + randomCode, EXPIRE_MINUTES, TimeUnit.MINUTES);
deleteRetSendCode(phoneNo);//删除用户重置密码,多次错误逻辑
return JsonResult.buildSuccessResult("发送成功", uniqueId); return JsonResult.buildSuccessResult("发送成功", uniqueId);
} catch (Exception e) { } catch (Exception e) {
LOGGER.error("发送语音短信验证码失败"); LOGGER.error("发送语音短信验证码失败");
return JsonResult.buildErrorStateResult("发送失败", null); return JsonResult.buildErrorStateResult("发送失败", null);
} }
} }
/**
* 快速登陆发送验证码新版
*/
@CaptchaNewValidator
@RequestMapping("/send_login_code_voice_new")
public JsonResult sendLoginSmsCodeNew(@RequestParam String phoneNo, @RequestParam(required = false) String registerFrom,
String usage, @RequestParam(required = false) String deviceId) {
if (StringUtils.isEmpty(usage) || !"6".equals(usage)) {
LOGGER.error("参数校验失败,用户登录语音验证码usage参数为{}", usage);
return JsonResult.buildErrorStateResult("参数校验失败.", null);
}
LOGGER.info("快速登陆-发送验证码, phoneNo:{}, registerFrom:{}", phoneNo, registerFrom);
return sendVerificationCode2VoiceNew(phoneNo, usage, deviceId);
}
/**
* 快速登陆发送短信验证码
*/
@CaptchaNewValidator
@RequestMapping("/send_login_code_new")
public JsonResult sendLoginCodeVoiceNew(@RequestParam String phoneNo, @RequestParam(required = false) String registerFrom, @RequestParam(required = false) String deviceId) {
LOGGER.info("快速登陆-发送验证码, phoneNo:{}, registerFrom:{}", phoneNo, registerFrom);
return sendVerificationCode2New(phoneNo, deviceId);
}
/**
* 新版本验证码
*/
private JsonResult sendVerificationCode2New(String phoneNo, String deviceId) {
if (!ValidationUtil.validatePhoneNo(phoneNo)) {
return JsonResult.buildErrorStateResult("手机号格式有误", null);
}
String verificationPhoneCountKey = Constants.REDIS_SMS_CODE_COUNT + phoneNo;
Long getPhoneVerificationCount = redisTemplate.opsForHash().increment(verificationPhoneCountKey, Constants.REDIS_SMS_CODE_COUNT, 1);
if (getPhoneVerificationCount > PHONE_MAX_PER_DAY) {
return JsonResult.buildErrorStateResult("今天已获取20次短信验证码,请使用语音验证码或明天再试", null);
}
String verificationIPCountKey = getIp();
if (!StringUtils.isEmpty(verificationIPCountKey)) {
verificationIPCountKey=Constants.REDIS_SMS_IP_COUNT+verificationIPCountKey;
Long getIPVerificationCount = redisTemplate.opsForHash().increment(verificationIPCountKey, Constants.REDIS_SMS_IP_COUNT, 1);
if (getIPVerificationCount > IP_MAX_PER_DAY) {
return JsonResult.buildErrorStateResult("您当前ip已经达到获取今天验证码上限", null);
}
}
if (!StringUtils.isEmpty(deviceId)) {
String verificationDeviceCountKey = Constants.REDIS_SMS_DEVICE_COUNT + deviceId;
Long getDeviceVerificationCount = redisTemplate.opsForHash().increment(verificationDeviceCountKey, Constants.REDIS_SMS_DEVICE_COUNT, 1);
if (getDeviceVerificationCount > DEVICE_MAX_PER_DAY) {
return JsonResult.buildErrorStateResult("您设备已经达到获取今天短信验证码上限", null);
}
}
String key = Constants.REDIS_PREFIX_VERIFICATION_CODE + phoneNo;
long expire = redisTemplate.getExpire(key, TimeUnit.MINUTES);
if (expire >= EXPIRE_MINUTES - 1) {
return JsonResult.buildSuccessResult("发送成功", null);
}
String randomCode = smsIsDebug ? "0000" : String.valueOf(random.nextInt(8999) + 1000);
String uniqueId = phoneNo + UUID.randomUUID().toString().replaceAll("-", "");
List<String> newList = new ArrayList<>();
newList.add(randomCode);
/*ConfirmableMsg confirmableMsg = new ConfirmableMsg(
uniqueId, newList, "1", "1", phoneNo
);*/
MsgParams message = new MsgParams(Collections.singletonList(2), phoneNo, "1", "1", Collections.singletonList(randomCode), uniqueId);
try {
//smsService.getSmsSender().sendConfirmableMessage(confirmableMsg);
smsService.getSmsSender().sendMsg(message);
redisTemplate.opsForValue().set(key, uniqueId + ":" + randomCode, EXPIRE_MINUTES, TimeUnit.MINUTES);
deleteRetSendCode(phoneNo);//删除用户重置密码,多次错误逻辑
if(needImageVlidate(verificationIPCountKey,deviceId,phoneNo)){
return JsonResult.buildSuccessResult("发送成功", uniqueId,0003L);
}
return JsonResult.buildSuccessResult("发送成功", uniqueId);
} catch (Exception e) {
LOGGER.error("发送短信验证码失败");
return JsonResult.buildErrorStateResult("发送失败", null);
}
}
/**
* 新版本语音验证码
*/
private JsonResult sendVerificationCode2VoiceNew(String phoneNo, String usage, String deviceId) {
String verificationCountKey = Constants.REDIS_VOICE_CODE_COUNT + phoneNo;
Long getVerificationCount = redisTemplate.opsForHash().increment(verificationCountKey, usage.toString(), 1);
if (getVerificationCount > PHONE_VOICE_MAX_PER_DAY) {
return JsonResult.buildErrorStateResult("今天已获取5次语音验证码,请使用短信验证码或明天再试", null);
}
String verificationIPCountKey = getIp();
if (!StringUtils.isEmpty(verificationIPCountKey)) {
verificationIPCountKey=Constants.REDIS_VOICE_IP_COUNT+verificationIPCountKey;
Long getIPVerificationCount = redisTemplate.opsForHash().increment(verificationIPCountKey, Constants.REDIS_VOICE_IP_COUNT, 1);
if (getIPVerificationCount > IP_MAX_PER_DAY) {
return JsonResult.buildErrorStateResult("您当前ip已经达到获取今天语音验证码上限", null);
}
}
if (!StringUtils.isEmpty(deviceId)) {
String verificationDeviceCountKey = Constants.REDIS_VOICE_DEVICE_COUNT + deviceId;
Long getDeviceVerificationCount = redisTemplate.opsForHash().increment(verificationDeviceCountKey, Constants.REDIS_VOICE_DEVICE_COUNT, 1);
if (getDeviceVerificationCount > DEVICE_MAX_PER_DAY) {
return JsonResult.buildErrorStateResult("您设备已经达到获取今天语音验证码上限", null);
}
}
redisTemplate.expire(verificationCountKey, DateUtils.getSeconds(), TimeUnit.SECONDS);
if (!ValidationUtil.validatePhoneNo(phoneNo)) {
return JsonResult.buildErrorStateResult("手机号格式有误", null);
}
String key = Constants.REDIS_PREFIX_VERIFICATION_CODE + phoneNo;
long expire = redisTemplate.getExpire(key, TimeUnit.MINUTES);
if (expire >= EXPIRE_MINUTES - 1) {
return JsonResult.buildSuccessResult("发送成功", null);
}
String randomCode = smsIsDebug ? "0000" : String.valueOf(random.nextInt(8999) + 1000);
String uniqueId = phoneNo + UUID.randomUUID().toString().replaceAll("-", "");
MsgParams message = new MsgParams(Collections.singletonList(4), phoneNo, "1", "4", Collections.singletonList(randomCode), uniqueId);
try {
smsService.getSmsSender().sendMsg(message);
redisTemplate.opsForValue().set(key, uniqueId + ":" + randomCode, EXPIRE_MINUTES, TimeUnit.MINUTES);
deleteRetSendCode(phoneNo);//删除用户重置密码,多次错误逻辑
if(needImageVlidate(verificationIPCountKey,deviceId,phoneNo)){
return JsonResult.buildSuccessResult("发送成功", uniqueId,0003L);
}
return JsonResult.buildSuccessResult("发送成功", uniqueId);
} catch (Exception e) {
LOGGER.error("发送语音短信验证码失败");
return JsonResult.buildErrorStateResult("发送失败", null);
}
}
/**
* 判断下次是否提示图形验证码
* @param clientIp
* @param deviceId
* @param phoneNo
* @return
*/
private boolean needImageVlidate(String clientIp, String deviceId, String phoneNo) {
boolean need = false;
String countIP = redisTemplate.opsForValue().get(IMAGE_IP_COUNT + clientIp);
String countDeviceId = redisTemplate.opsForValue().get(IMAGE_DEVICEID_COUNT + deviceId);
String countPhoneNo = redisTemplate.opsForValue().get(IMAGE_PHONE_COUNT + phoneNo);
Long ip = StringUtils.isBlank(countIP) ? 1L : Long.valueOf(countIP);
Long devId = StringUtils.isBlank(countDeviceId) ? 1L : Long.valueOf(countDeviceId);
Long phNo = StringUtils.isBlank(countPhoneNo) ? 1L : Long.valueOf(countPhoneNo);
if (ip >= Constants.Image_Need_Count || devId >= Constants.Image_Need_Count || phNo >= Constants.Image_Need_Count ) {
need = true;
}
return need;
}
/**
* 删除用户重置密码是短信验证错误
* @param phoneNo
*/
private void deleteRetSendCode(String phoneNo){
String verificationCountKey = Constants.REDIS_VERIFICATION_COUNT+phoneNo;
redisTemplate.opsForHash().delete(verificationCountKey, Constants.REDIS_VERIFICATION_COUNT);
}
} }
...@@ -201,7 +201,7 @@ public class UserController implements IBaseController { ...@@ -201,7 +201,7 @@ public class UserController implements IBaseController {
} }
if (!smsService.validateFastLoginVerificationCode(phoneNo, verificationCode)) { if (!smsService.validateFastLoginVerificationCode(phoneNo, verificationCode)) {
LOGGER.info("用户快速登录,验证码校验失败,phoneNo:{} , verificationCode:{}", phoneNo, verificationCode); LOGGER.info("用户快速登录,验证码校验失败,phoneNo:{} , verificationCode:{}", phoneNo, verificationCode);
result.put("fail", JsonResult.buildErrorStateResult("验证码错误", null)); result.put("fail", JsonResult.buildErrorStateResult("验证码不正确", null));
} }
result.put("success", JsonResult.buildSuccessResult("", phoneNo)); result.put("success", JsonResult.buildSuccessResult("", phoneNo));
return result; return result;
...@@ -356,6 +356,11 @@ public class UserController implements IBaseController { ...@@ -356,6 +356,11 @@ public class UserController implements IBaseController {
return JsonResult.buildErrorStateResult("密码应为6-12位", null); return JsonResult.buildErrorStateResult("密码应为6-12位", null);
} }
if (!smsService.validRegisterOrResetPasswdVerificationCode(phoneNo, verificationCode)) { if (!smsService.validRegisterOrResetPasswdVerificationCode(phoneNo, verificationCode)) {
if(needRetSendCode(phoneNo)){
String key = Constants.REDIS_PREFIX_VERIFICATION_CODE + phoneNo;
stringRedisTemplate.delete(key);
return JsonResult.buildErrorStateResult("错误次数过多,请重新获取短信验证码", null);
}
return JsonResult.buildErrorStateResult("短信验证码错误", null); return JsonResult.buildErrorStateResult("短信验证码错误", null);
} }
if (!userService.resetPassword(phoneNo, password)) { if (!userService.resetPassword(phoneNo, password)) {
...@@ -367,6 +372,21 @@ public class UserController implements IBaseController { ...@@ -367,6 +372,21 @@ public class UserController implements IBaseController {
return JsonResult.buildSuccessResult(null, null); return JsonResult.buildSuccessResult(null, null);
} }
/**
* 是否需要重新发送短信验证码
* @param phoneNo
* @return
*/
private boolean needRetSendCode(@RequestParam String phoneNo) {
boolean needRetSend=false;
String verificationCountKey = Constants.REDIS_VERIFICATION_COUNT+phoneNo;
Long getVerificationCount = stringRedisTemplate.opsForHash().increment(verificationCountKey, Constants.REDIS_VERIFICATION_COUNT, 1);
if(getVerificationCount>5){
return needRetSend=true;
}
return needRetSend;
}
/** /**
* 检查token是否已经过期不存在了 * 检查token是否已经过期不存在了
* *
......
...@@ -21,8 +21,6 @@ import java.sql.Timestamp; ...@@ -21,8 +21,6 @@ import java.sql.Timestamp;
@Table(name = "user", uniqueConstraints = @UniqueConstraint(columnNames = "phone_no")) @Table(name = "user", uniqueConstraints = @UniqueConstraint(columnNames = "phone_no"))
public class User implements Serializable { public class User implements Serializable {
private static final long serialVersionUID = -1L;
@Id @Id
@Column(name = "id") @Column(name = "id")
@GeneratedValue(strategy = GenerationType.IDENTITY) @GeneratedValue(strategy = GenerationType.IDENTITY)
...@@ -56,6 +54,8 @@ public class User implements Serializable { ...@@ -56,6 +54,8 @@ public class User implements Serializable {
@Column(name = "updated_at") @Column(name = "updated_at")
@JSONField(serializeUsing = Timestamp2LongConverter.class) @JSONField(serializeUsing = Timestamp2LongConverter.class)
private Timestamp updatedAt; private Timestamp updatedAt;
private static final long serialVersionUID = -1L;
public XUser toXUser() { public XUser toXUser() {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment