Commit 7da31688 authored by 技术部-任文超's avatar 技术部-任文超

重构QG-Test(量化派图形验证码)的service和校验切面

parent adb404b0
...@@ -98,5 +98,7 @@ public interface Constants { ...@@ -98,5 +98,7 @@ public interface Constants {
String FN_GEETEST_CHALLENGE = "geetest_challenge"; String FN_GEETEST_CHALLENGE = "geetest_challenge";
String FN_GEETEST_VALIDATE = "geetest_validate"; String FN_GEETEST_VALIDATE = "geetest_validate";
String FN_GEETEST_SECCODE = "geetest_seccode"; String FN_GEETEST_SECCODE = "geetest_seccode";
String GEETEST_CAPTCHA_STATUS = "success"; String TEST_PARAM = "testType";
String TEST_TYPE_GT = "gt";
String TEST_TYPE_QG = "qg";
} }
...@@ -2,7 +2,8 @@ package cn.quantgroup.xyqb.aspect.captcha; ...@@ -2,7 +2,8 @@ package cn.quantgroup.xyqb.aspect.captcha;
import cn.quantgroup.xyqb.Constants; import cn.quantgroup.xyqb.Constants;
import cn.quantgroup.xyqb.model.JsonResult; import cn.quantgroup.xyqb.model.JsonResult;
import cn.quantgroup.xyqb.service.captcha.geetest.IGeetestCaptchaService; import cn.quantgroup.xyqb.service.captcha.IGeetestCaptchaService;
import cn.quantgroup.xyqb.service.captcha.IQuantgroupCaptchaService;
import cn.quantgroup.xyqb.thirdparty.jcaptcha.AbstractManageableImageCaptchaService; import cn.quantgroup.xyqb.thirdparty.jcaptcha.AbstractManageableImageCaptchaService;
import cn.quantgroup.xyqb.util.IPUtil; import cn.quantgroup.xyqb.util.IPUtil;
import cn.quantgroup.xyqb.util.PasswordUtil; import cn.quantgroup.xyqb.util.PasswordUtil;
...@@ -22,6 +23,7 @@ import org.springframework.web.context.request.ServletRequestAttributes; ...@@ -22,6 +23,7 @@ import org.springframework.web.context.request.ServletRequestAttributes;
import javax.annotation.Resource; import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import java.util.UUID; import java.util.UUID;
...@@ -32,29 +34,17 @@ import java.util.UUID; ...@@ -32,29 +34,17 @@ import java.util.UUID;
@Component @Component
@Slf4j @Slf4j
public class CaptchaNewValidateAdvisor { public class CaptchaNewValidateAdvisor {
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__";
@Resource @Resource
private IGeetestCaptchaService geetestCaptchaService; private IGeetestCaptchaService geetestCaptchaService;
@Resource @Resource
@Qualifier("customCaptchaService") private IQuantgroupCaptchaService quantgroupCaptchaService;
private AbstractManageableImageCaptchaService imageCaptchaService;
/**
* 自动化测试忽略验证码
*/
@Value("${xyqb.auth.captcha.autotest.enable:false}")
private boolean autoTestCaptchaEnabled;
/** /**
* 图形验证码切面 * 图形验证码切面
*/ */
@Pointcut("@annotation(cn.quantgroup.xyqb.aspect.captcha.CaptchaNewValidator)") @Pointcut("@annotation(cn.quantgroup.xyqb.aspect.captcha.CaptchaNewValidator)")
private void needCaptchaValidate() { private void needCaptchaValidate(){}
}
/** /**
* 在受图形验证码保护的接口方法执行前, 执行图形验证码校验 * 在受图形验证码保护的接口方法执行前, 执行图形验证码校验
...@@ -66,53 +56,48 @@ public class CaptchaNewValidateAdvisor { ...@@ -66,53 +56,48 @@ public class CaptchaNewValidateAdvisor {
@Around("needCaptchaValidate()") @Around("needCaptchaValidate()")
private Object doCaptchaValidate(ProceedingJoinPoint pjp) throws Throwable { private Object doCaptchaValidate(ProceedingJoinPoint pjp) throws Throwable {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
// 验证码类别:gt - 极验,qg - 量化派图形验证
if (Constants.GT_CAPTCHA_UNUSER.equals(request.getParameter(Constants.GEETEST_CAPTCHA_STATUS))) { if (Objects.equals(Constants.TEST_TYPE_GT, request.getParameter(Constants.TEST_PARAM))) {
//极验不可用,用QG if(gtValid(request)){
if (isQuantgroupCaptchaValidateSuccess(request)) {
return pjp.proceed(); return pjp.proceed();
} else { }else{
return JsonResult.buildErrorStateResult("图形验证码有误", ""); return JsonResult.buildErrorStateResult("验证码有误", "");
} }
} else { } else {
String challenge = request.getParameter(Constants.FN_GEETEST_CHALLENGE); if (qgValid(request)) {
String validate = request.getParameter(Constants.FN_GEETEST_VALIDATE); return pjp.proceed();
String seccode = request.getParameter(Constants.FN_GEETEST_SECCODE); } else {
String phoneNo = request.getParameter("phoneNo"); return JsonResult.buildErrorStateResult("验证码有误", "");
String clientType = request.getParameter("clientType"); }
geetestCaptchaService.captchaValidate(clientType, PasswordUtil.MD5(phoneNo), IPUtil.getRemoteIP(request), challenge, validate, seccode);
log.info("使用极验二次验证,phoneNo:{}", phoneNo);
} }
return pjp.proceed();
} }
private Boolean isQuantgroupCaptchaValidateSuccess(HttpServletRequest request) throws Throwable { /**
String registerFrom = Optional.ofNullable(request.getParameter("registerFrom")).orElse(""); * 极验校验
String captchaId = Optional.ofNullable(request.getParameter("captchaId")).orElse(""); * @param request
String captchaValue = request.getParameter("captchaValue"); * @return
if (isSkipCaptchaValidate(captchaId, captchaValue)) { * @throws Throwable
log.info("使用超级图形验证码校验, registerFrom={}, clientIp={}", registerFrom, IPUtil.getRemoteIP(request)); */
return Boolean.TRUE; private boolean gtValid(HttpServletRequest request) {
} String challenge = request.getParameter(Constants.FN_GEETEST_CHALLENGE);
String validate = request.getParameter(Constants.FN_GEETEST_VALIDATE);
Boolean validCaptcha = false; String seccode = request.getParameter(Constants.FN_GEETEST_SECCODE);
if (StringUtils.isNotBlank(captchaValue)) { String phoneNo = request.getParameter("phoneNo");
// 忽略用户输入的大小写 String clientType = request.getParameter("clientType");
String captcha = StringUtils.lowerCase(captchaValue); log.info("使用极验二次验证,phoneNo:{}", phoneNo);
// 验证码校验 return 0 == geetestCaptchaService.captchaValidate(clientType, PasswordUtil.MD5(phoneNo), IPUtil.getRemoteIP(request), challenge, validate, seccode);
try {
validCaptcha = imageCaptchaService.validateResponseForID(Constants.IMAGE_CAPTCHA_KEY + captchaId, captcha);
} catch (CaptchaServiceException ex) {
log.error("验证码校验异常, {}, {}", ex.getMessage(), ex);
}
}
return validCaptcha;
} }
private boolean isSkipCaptchaValidate(String captchaId, Object captchaValue) { /**
// 如果启用了超级验证码功能, 检查超级验证码, 超级验证码区分大小写 * 量化派图形码校验
return autoTestCaptchaEnabled || org.apache.commons.lang3.StringUtils.equals(SUPER_CAPTCHA_ID, String.valueOf(captchaId)) * @param request
&& org.apache.commons.lang3.StringUtils.equals(SUPER_CAPTCHA, String.valueOf(captchaValue)); * @return
* @throws Throwable
*/
private boolean qgValid(HttpServletRequest request) {
String captchaId = Optional.ofNullable(request.getParameter("captchaId")).orElse("");
String captchaValue = request.getParameter("captchaValue");
return quantgroupCaptchaService.validCaptcha(captchaId, captchaValue);
} }
} }
...@@ -4,8 +4,8 @@ import cn.quantgroup.xyqb.Constants; ...@@ -4,8 +4,8 @@ import cn.quantgroup.xyqb.Constants;
import cn.quantgroup.xyqb.aspect.captcha.CaptchaNewValidator; import cn.quantgroup.xyqb.aspect.captcha.CaptchaNewValidator;
import cn.quantgroup.xyqb.aspect.logcaller.LogHttpCaller; import cn.quantgroup.xyqb.aspect.logcaller.LogHttpCaller;
import cn.quantgroup.xyqb.model.JsonResult; import cn.quantgroup.xyqb.model.JsonResult;
import cn.quantgroup.xyqb.service.captcha.geetest.IGeetestCaptchaService; import cn.quantgroup.xyqb.service.captcha.IGeetestCaptchaService;
import cn.quantgroup.xyqb.service.captcha.qg.IQuantgroupCaptchaService; import cn.quantgroup.xyqb.service.captcha.IQuantgroupCaptchaService;
import cn.quantgroup.xyqb.util.IPUtil; import cn.quantgroup.xyqb.util.IPUtil;
import cn.quantgroup.xyqb.util.PasswordUtil; import cn.quantgroup.xyqb.util.PasswordUtil;
import cn.quantgroup.xyqb.util.ValidationUtil; import cn.quantgroup.xyqb.util.ValidationUtil;
......
package cn.quantgroup.xyqb.service.captcha.geetest; package cn.quantgroup.xyqb.service.captcha;
/** /**
* @author xufei on 2018/1/30. * @author xufei on 2018/1/30.
......
package cn.quantgroup.xyqb.service.captcha.qg; package cn.quantgroup.xyqb.service.captcha;
import java.util.Locale; import java.util.Locale;
import java.util.Map;
/** /**
* @author xufei on 2018/1/30. * @author xufei on 2018/1/30.
*/ */
public interface IQuantgroupCaptchaService { public interface IQuantgroupCaptchaService {
/** /**
* QG获取验证码 * 获取QG验证码
*
* @param locale * @param locale
* @return * @return
* @throws Exception EX * @throws Exception EX
*/ */
String fetchCaptcha(Locale locale) throws Exception; Map<String, String> fetchCaptcha(Locale locale);
/**
* 校验QG验证码
* @param key
* @param code
* @return
*/
boolean validCaptcha(String key, String code);
} }
package cn.quantgroup.xyqb.service.captcha.geetest; package cn.quantgroup.xyqb.service.captcha.impl;
import cn.quantgroup.xyqb.service.captcha.geetest.sdk.GeetestLib; import cn.quantgroup.xyqb.service.captcha.IGeetestCaptchaService;
import cn.quantgroup.xyqb.service.captcha.impl.sdk.GeetestLib;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
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;
......
package cn.quantgroup.xyqb.service.captcha.qg; package cn.quantgroup.xyqb.service.captcha.impl;
import cn.quantgroup.xyqb.Constants; import cn.quantgroup.xyqb.Constants;
import cn.quantgroup.xyqb.model.JsonResult;
import cn.quantgroup.xyqb.service.captcha.IQuantgroupCaptchaService;
import cn.quantgroup.xyqb.thirdparty.jcaptcha.AbstractManageableImageCaptchaService; import cn.quantgroup.xyqb.thirdparty.jcaptcha.AbstractManageableImageCaptchaService;
import com.octo.captcha.service.CaptchaServiceException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64; import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.StringUtils;
import org.json.JSONObject; import org.json.JSONObject;
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;
...@@ -12,40 +17,57 @@ import org.springframework.stereotype.Service; ...@@ -12,40 +17,57 @@ import org.springframework.stereotype.Service;
import javax.imageio.ImageIO; import javax.imageio.ImageIO;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Locale; import java.util.Locale;
import java.util.Map;
import java.util.UUID; import java.util.UUID;
/** /**
* @author xufei on 2018/1/30. * @author xufei on 2018/1/30.
*/ */
@Slf4j
@Service @Service
public class QuantgroupCaptchaServiceImpl implements IQuantgroupCaptchaService { public class QuantgroupCaptchaServiceImpl implements IQuantgroupCaptchaService {
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";
/**
* 自动化测试忽略验证码
*/
@Value("${xyqb.auth.captcha.autotest.enable:false}")
private boolean autoTestCaptchaEnabled;
@Autowired @Autowired
@Qualifier("customCaptchaService") @Qualifier("customCaptchaService")
private AbstractManageableImageCaptchaService imageCaptchaService; private AbstractManageableImageCaptchaService imageCaptchaService;
@Override @Override
public String fetchCaptcha(Locale locale) throws Exception { public Map<String, String> fetchCaptcha(Locale locale) {
Map<String, String> data = new HashMap<>();
data.put(Constants.TEST_PARAM, Constants.TEST_TYPE_QG);
String imageId = UUID.randomUUID().toString(); String imageId = UUID.randomUUID().toString();
BufferedImage challenge = imageCaptchaService.getImageChallengeForID(Constants.IMAGE_CAPTCHA_KEY + imageId, locale); BufferedImage challenge = imageCaptchaService.getImageChallengeForID(Constants.IMAGE_CAPTCHA_KEY + imageId, locale);
ByteArrayOutputStream jpegOutputStream = new ByteArrayOutputStream(); ByteArrayOutputStream jpegOutputStream = new ByteArrayOutputStream();
ImageIO.write(challenge, IMAGE_FORMAT_PNG, jpegOutputStream); try {
ImageIO.write(challenge, IMAGE_FORMAT_PNG, jpegOutputStream);
} catch (IOException e) {
log.error("生成QG图形验证码", e);
return data;
}
String imageBase64 = Base64.encodeBase64String(jpegOutputStream.toByteArray()); String imageBase64 = Base64.encodeBase64String(jpegOutputStream.toByteArray());
data.put("imageId", imageId);
data.put("image", String.format(IMG_BASE64_PATTREN, imageBase64));
return data;
}
JSONObject jsonObject = new JSONObject(); @Override
jsonObject.put("success","0"); public boolean validCaptcha(String key, String code) {
jsonObject.put("imageId", imageId); Boolean validCaptcha = false;
jsonObject.put("image", String.format(IMG_BASE64_PATTREN, imageBase64)); if (StringUtils.isNotBlank(key) && StringUtils.isNotBlank(code)) {
return jsonObject.toString(); // 验证码校验(忽略用户输入的大小写)
try {
validCaptcha = imageCaptchaService.validateResponseForID(Constants.IMAGE_CAPTCHA_KEY + key, code.toLowerCase());
} catch (CaptchaServiceException e) {
log.error("校验QG图形验证码:key:{}, code:{}", key, code, e);
}
}
return validCaptcha;
} }
} }
package cn.quantgroup.xyqb.service.captcha.geetest.sdk; package cn.quantgroup.xyqb.service.captcha.impl.sdk;
import cn.quantgroup.xyqb.Constants; import cn.quantgroup.xyqb.Constants;
import org.json.JSONException; import org.json.JSONException;
......
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