修改增加验证码逻辑

parent 85896f3a
...@@ -25,6 +25,7 @@ public interface Constants { ...@@ -25,6 +25,7 @@ public interface Constants {
String REDIS_PREFIX_VERIFICATION_CODE = "verificationCode_"; String REDIS_PREFIX_VERIFICATION_CODE = "verificationCode_";
String REDIS_PREFIX_VERIFICATION_VOICE_CODE = "verificationCode_voice_";
String REDIS_VOICE_CODE_COUNT = "voice_verification_code_count:"; String REDIS_VOICE_CODE_COUNT = "voice_verification_code_count:";
/** /**
......
...@@ -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,48 @@ import java.io.IOException; ...@@ -22,41 +30,48 @@ 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 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,7 +91,52 @@ public class ImageCaptchaController implements IBaseController { ...@@ -76,7 +91,52 @@ 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);
}
/**
* 新版本获取验证码
* 默认匹配 GET /captcha, 提供4位数字和字母混合图片验证码
*/
@RequestMapping(value = "/captcha_new")
public JsonResult fetchCaptchaNew(HttpServletRequest request, @ModelAttribute("clientIp") String clientIp) {
if (StringUtils.isBlank(clientIp)) {
return JsonResult.buildErrorStateResult("", "fail");
}
Long count= countIP(clientIp);
if(count>5){
String imageId = UUID.randomUUID().toString();
BufferedImage challenge = imageCaptchaService.getImageChallengeForID(Constants.IMAGE_CAPTCHA_KEY + imageId, request.getLocale());
ByteArrayOutputStream jpegOutputStream = new ByteArrayOutputStream();
try {
boolean write = ImageIO.write(challenge, IMAGE_FORMAT_PNG, jpegOutputStream);
} catch (IOException e) {
e.printStackTrace();
return JsonResult.buildErrorStateResult("", "fail");
}
String imageBase64 = Base64.encodeBase64String(jpegOutputStream.toByteArray());
Map<String, String> data = new HashMap<>();
data.put("imageId", imageId);
data.put("image", String.format(IMG_BASE64_PATTREN, imageBase64));
return JsonResult.buildSuccessResult("", data);
}
return JsonResult.buildSuccessResult("", "success");
}
private Long countIP(@ModelAttribute("clientIp") String clientIp) {
Long count=1L;
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);
}
} }
/** /**
...@@ -84,8 +144,39 @@ public class ImageCaptchaController implements IBaseController { ...@@ -84,8 +144,39 @@ public class ImageCaptchaController implements IBaseController {
*/ */
@CaptchaValidator @CaptchaValidator
@RequestMapping("/verification_image_code") @RequestMapping("/verification_image_code")
public JsonResult verificationImageCode() { public JsonResult verificationImageCode(HttpServletRequest request, @RequestParam String captchaId, @RequestParam Object captchaValue) {
return JsonResult.buildSuccessResult("", null); Boolean validCaptcha = false;
String registerFrom = Optional.ofNullable(request.getParameter("registerFrom")).orElse("");
if (shouldSkipCaptchaValidate(registerFrom, captchaId, captchaValue)) {
LOGGER.info("使用超级图形验证码校验, registerFrom={}, clientIp={}", registerFrom, request.getRemoteAddr());
validCaptcha = true;
return JsonResult.buildSuccessResult("", validCaptcha);
}
JsonResult result = JsonResult.buildSuccessResult("图形验证码错误, 请重新输入", "");
result.setBusinessCode("0002");
if (captchaValue != null) {
String captcha = String.valueOf(captchaValue);
// 忽略用户输入的大小写
captcha = StringUtils.lowerCase(captcha);
// 验证码校验
try {
validCaptcha = imageCaptchaService.validateResponseForID(Constants.IMAGE_CAPTCHA_KEY + captchaId, captcha);
} catch (CaptchaServiceException ex) {
LOGGER.error("验证码校验异常, {}, {}", ex.getMessage(), ex);
}
}
return JsonResult.buildSuccessResult("", validCaptcha);
} }
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));
}
} }
...@@ -176,4 +176,93 @@ public class SmsController { ...@@ -176,4 +176,93 @@ public class SmsController {
return JsonResult.buildErrorStateResult("发送失败", null); return JsonResult.buildErrorStateResult("发送失败", null);
} }
} }
/**
* 快速登陆发送验证码新版
*/
@RequestMapping("/send_login_code_voice_new")
public JsonResult sendLoginSmsCodeNew(@RequestParam String phoneNo, @RequestParam(required = false) String registerFrom,
String usage) {
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);
}
/**
* 快速登陆发送短信验证码
*/
@RequestMapping("/send_login_code_new")
public JsonResult sendLoginCodeVoiceNew(@RequestParam String phoneNo, @RequestParam(required = false) String registerFrom) {
LOGGER.info("快速登陆-发送验证码, phoneNo:{}, registerFrom:{}", phoneNo, registerFrom);
return sendVerificationCode2New(phoneNo);
}
/**
* 新版本验证码
* @param phoneNo
* @return
*/
private JsonResult sendVerificationCode2New(String phoneNo) {
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("-", "");
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);
return JsonResult.buildSuccessResult("发送成功", uniqueId);
} catch (Exception e) {
LOGGER.error("发送短信验证码失败");
return JsonResult.buildErrorStateResult("发送失败", null);
}
}
/**
* 新版本语音验证码
* @param phoneNo
* @return
*/
private JsonResult sendVerificationCode2VoiceNew(String phoneNo, String usage) {
String verificationCountKey = Constants.REDIS_VOICE_CODE_COUNT + phoneNo;
Long getVerificationCount = redisTemplate.opsForHash().increment(verificationCountKey, usage.toString(), 1);
if (getVerificationCount > 5) {
return JsonResult.buildErrorStateResult("今天已获取5次语音验证码,请使用短信验证码或明天再试", null);
}
redisTemplate.expire(verificationCountKey, DateUtils.getSeconds(), TimeUnit.SECONDS);
if (!ValidationUtil.validatePhoneNo(phoneNo)) {
return JsonResult.buildErrorStateResult("手机号格式有误", null);
}
String key = Constants.REDIS_PREFIX_VERIFICATION_VOICE_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);
return JsonResult.buildSuccessResult("发送成功", uniqueId);
} catch (Exception e) {
LOGGER.error("发送语音短信验证码失败");
return JsonResult.buildErrorStateResult("发送失败", null);
}
}
} }
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