Commit 977ac720 authored by 贷前—徐菲's avatar 贷前—徐菲

极验

parent c88d1ea1
...@@ -338,6 +338,11 @@ ...@@ -338,6 +338,11 @@
<artifactId>sentry-spring</artifactId> <artifactId>sentry-spring</artifactId>
<version>1.6.3</version> <version>1.6.3</version>
</dependency> </dependency>
<dependency>
<groupId>com.vaadin.external.google</groupId>
<artifactId>android-json</artifactId>
<version>0.0.20131108.vaadin1</version>
</dependency>
</dependencies> </dependencies>
......
...@@ -94,4 +94,10 @@ public interface Constants { ...@@ -94,4 +94,10 @@ public interface Constants {
String AES_KEY = "ScnmRBhuQpo9kBdn"; String AES_KEY = "ScnmRBhuQpo9kBdn";
String GEETEST_ID = "002bc30ff1eef93e912f45814945e752";
String GEETEST_KEY = "4193a0e3247b82a26f563d595c447b1a";
boolean NEW_FAIL_BACK = true;
String GT_SERVER_STATUS_SESSION_KEY = "gt_server_status";
String GT_SERVER_STATUS_USABLE = "1";
Long GT_SERVER_STATUS_EXIST_REDIS = 2L;
} }
package cn.quantgroup.xyqb.aspect.captcha;
import cn.quantgroup.xyqb.Constants;
import cn.quantgroup.xyqb.model.JsonResult;
import cn.quantgroup.xyqb.service.captcha.geetest.IGeetestCaptchaService;
import cn.quantgroup.xyqb.thirdparty.jcaptcha.AbstractManageableImageCaptchaService;
import cn.quantgroup.xyqb.util.IPUtil;
import com.octo.captcha.service.CaptchaServiceException;
import lombok.extern.slf4j.Slf4j;
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.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;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.nio.charset.Charset;
import java.util.Optional;
import java.util.UUID;
/**
* @author xufei on 2018/1/30.
*/
@Aspect
@Component
@Slf4j
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
private IGeetestCaptchaService geetestCaptchaService;
@Resource
@Qualifier("stringRedisTemplate")
private RedisTemplate<String, String> stringRedisTemplate;
@Resource
@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 needCaptchaValidate() {
}
/**
* 在受图形验证码保护的接口方法执行前, 执行图形验证码校验
*
* @param pjp pjp
* @return
* @throws Throwable
* @return
*/
@Around("needCaptchaValidate()")
private Object doCaptchaValidate(ProceedingJoinPoint pjp) throws Throwable {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
//测试环境使用QG图形验证码
if(autoTestCaptchaEnabled){
quantgroupCaptchaValidate(pjp,request);
}
String phoneNo = Optional.ofNullable(request.getParameter("phoneNo")).orElse("");
String captchaKey = Constants.GT_SERVER_STATUS_SESSION_KEY + phoneNo;
if (stringRedisTemplate.hasKey(captchaKey) && Constants.GT_SERVER_STATUS_USABLE.equals(stringRedisTemplate.opsForValue().get(captchaKey))) {
geetestCaptchaService.verifyLogin(phoneNo, request);
log.info("使用极验验证码,phoneNo:{}",phoneNo);
return pjp.proceed();
} else {
return quantgroupCaptchaValidate(pjp,request);
}
}
private Object quantgroupCaptchaValidate(ProceedingJoinPoint pjp,HttpServletRequest request) throws Throwable {
String registerFrom = Optional.ofNullable(request.getParameter("registerFrom")).orElse("");
String captchaId = Optional.ofNullable(request.getParameter("captchaId")).orElse("");
String captchaValue = request.getParameter("captchaValue");
if (isSkipCaptchaValidate(captchaId, captchaValue)) {
log.info("使用超级图形验证码校验, registerFrom={}, clientIp={}", registerFrom, IPUtil.getRemoteIP(request));
return pjp.proceed();
}
return verifyCaptchaOnline(pjp, captchaId, captchaValue);
}
private Object verifyCaptchaOnline(ProceedingJoinPoint pjp, String captchaId, String captchaValue) throws Throwable {
JsonResult result = JsonResult.buildSuccessResult("图形验证码不正确", "");
result.setBusinessCode("0002");
if (org.apache.commons.lang3.StringUtils.isNotBlank(captchaValue)) {
// 忽略用户输入的大小写
String captcha = org.apache.commons.lang3.StringUtils.lowerCase(captchaValue);
// 验证码校验
Boolean validCaptcha = false;
try {
validCaptcha = imageCaptchaService.validateResponseForID(Constants.IMAGE_CAPTCHA_KEY + captchaId, captcha);
} catch (CaptchaServiceException ex) {
log.error("验证码校验异常, {}, {}", ex.getMessage(), ex);
}
if (validCaptcha) {
return pjp.proceed();
}
}
return result;
}
private boolean isSkipCaptchaValidate(String captchaId, Object captchaValue) {
// 如果启用了超级验证码功能, 检查超级验证码, 超级验证码区分大小写
return autoTestCaptchaEnabled || org.apache.commons.lang3.StringUtils.equals(SUPER_CAPTCHA_ID, String.valueOf(captchaId)) && org.apache.commons.lang3.StringUtils.equals(SUPER_CAPTCHA, String.valueOf(captchaValue));
}
}
package cn.quantgroup.xyqb.aspect.captcha;
import java.lang.annotation.*;
/**
* @author xufei on 2018/1/30.
*/
@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CaptchaNewValidator {
}
package cn.quantgroup.xyqb.controller.external.captcha;
import cn.quantgroup.xyqb.Constants;
import cn.quantgroup.xyqb.aspect.captcha.CaptchaNewValidator;
import cn.quantgroup.xyqb.aspect.logcaller.LogHttpCaller;
import cn.quantgroup.xyqb.model.JsonResult;
import cn.quantgroup.xyqb.service.captcha.geetest.IGeetestCaptchaService;
import cn.quantgroup.xyqb.service.captcha.geetest.sdk.GeetestLib;
import cn.quantgroup.xyqb.service.captcha.qg.IQuantgroupCaptchaService;
import cn.quantgroup.xyqb.util.ValidationUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
/**
* @author xufei on 2018/1/30.
*/
@Slf4j
@RestController
@RequestMapping("/api")
public class NewCaptchaController {
@Resource
private IGeetestCaptchaService geetestCaptchaService;
@Resource
private IQuantgroupCaptchaService quantgroupCaptchaService;
@LogHttpCaller
@RequestMapping(value = "/newCaptcha")
public JsonResult getCaptcha(HttpServletRequest request, String phoneNo) {
if (!ValidationUtil.validatePhoneNo(phoneNo)) {
return JsonResult.buildErrorStateResult("手机号格式错误", null);
}
log.info("[newCaptcha]获取验证码,phoneNo:{}", phoneNo);
GeetestLib gtSdk = geetestCaptchaService.getGeetestSdk();
if (geetestCaptchaService.getGeetestServerStatus(phoneNo, request, gtSdk) == Integer.parseInt(Constants.GT_SERVER_STATUS_USABLE)) {
log.info("[newCaptcha]极验可用,phoneNo:{}", phoneNo);
return JsonResult.buildSuccessResult("", geetestCaptchaService.startCaptcha(gtSdk));
} else {
try {
return JsonResult.buildSuccessResult("", quantgroupCaptchaService.fetchCaptcha(request));
} catch (Exception e) {
log.error("获取验证码失败e:{}", e);
return JsonResult.buildErrorStateResult("", "fail");
}
}
}
@CaptchaNewValidator
@RequestMapping("/new_verification_image_code")
public JsonResult verificationImageCode() {
return JsonResult.buildSuccessResult("", null);
}
}
...@@ -18,7 +18,7 @@ public class UserAuthorized { ...@@ -18,7 +18,7 @@ public class UserAuthorized {
@Id @Id
@Column(name = "id") @Column(name = "id")
@GeneratedValue(generator = "uuid") @GeneratedValue(generator = "uuid")
@GenericGenerator(name ="uuid" , strategy = "uuid") @GenericGenerator(name ="uuid" , strategy = "org.hibernate.id.UUIDGenerator")
private String id; private String id;
@Column(name = "user_uuid") @Column(name = "user_uuid")
......
package cn.quantgroup.xyqb.service.captcha.geetest;
import cn.quantgroup.xyqb.Constants;
import cn.quantgroup.xyqb.service.captcha.geetest.sdk.GeetestLib;
import cn.quantgroup.xyqb.util.IPUtil;
import cn.quantgroup.xyqb.util.PasswordUtil;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.concurrent.TimeUnit;
/**
* @author xufei on 2018/1/30.
*/
@Service
public class GeetestCaptchaServiceImpl implements IGeetestCaptchaService {
@Resource
@Qualifier("stringRedisTemplate")
private RedisTemplate<String, String> stringRedisTemplate;
@Override
public GeetestLib getGeetestSdk() {
return new GeetestLib(Constants.GEETEST_ID, Constants.GEETEST_KEY, Constants.NEW_FAIL_BACK);
}
@Override
public int getGeetestServerStatus(String phoneNo, HttpServletRequest request, GeetestLib gtSdk) {
HashMap<String, String> param = getParam(phoneNo, request);
int gtServerStatus = gtSdk.preProcess(param);
stringRedisTemplate.opsForValue().set(Constants.GT_SERVER_STATUS_SESSION_KEY + phoneNo,
Integer.toString(gtServerStatus), Constants.GT_SERVER_STATUS_EXIST_REDIS, TimeUnit.MINUTES);
//进行验证预处理
return gtServerStatus;
}
private HashMap<String, String> getParam(String phoneNo, HttpServletRequest request) {
HashMap<String, String> param = new HashMap<>();
param.put("user_id", PasswordUtil.MD5(phoneNo));
param.put("client_type", "H5");
param.put("ip_address", IPUtil.getRemoteIP(request));
return param;
}
@Override
public String startCaptcha(GeetestLib gtSdk) {
return gtSdk.getResponseStr();
}
@Override
public int verifyLogin(String phoneNo, HttpServletRequest request) {
HashMap<String, String> param = getParam(phoneNo, request);
String challenge = request.getParameter(GeetestLib.fn_geetest_challenge);
String validate = request.getParameter(GeetestLib.fn_geetest_validate);
String seccode = request.getParameter(GeetestLib.fn_geetest_seccode);
return getGeetestSdk().enhencedValidateRequest(challenge, validate, seccode, param);
}
}
package cn.quantgroup.xyqb.service.captcha.geetest;
import cn.quantgroup.xyqb.service.captcha.geetest.sdk.GeetestLib;
import javax.servlet.http.HttpServletRequest;
/**
* @author xufei on 2018/1/30.
*/
public interface IGeetestCaptchaService {
/**
* 获取极验的sdk
*
* @return
*/
GeetestLib getGeetestSdk();
/**
* 获取geetest服务器可用的状态
*
* @param phoneNo 用户的手机号
* @param request rq
* @param gtSdk sdk
* @return 成功返回1, 失败返回0
*/
int getGeetestServerStatus(String phoneNo, HttpServletRequest request, GeetestLib gtSdk);
/**
* 获取geetest的验证码
*
* @param gtSdk sdk
* @return
*/
String startCaptcha(GeetestLib gtSdk);
/**
* 二次验证
*
* @param phoneNo 参数
* @param request rq
* @return 验证结果, 1表示验证成功0表示验证失败
*/
int verifyLogin(String phoneNo, HttpServletRequest request);
}
package cn.quantgroup.xyqb.service.captcha.qg;
import javax.servlet.http.HttpServletRequest;
import java.util.Map;
/**
* @author xufei on 2018/1/30.
*/
public interface IQuantgroupCaptchaService {
/**
* QG获取验证码
*
* @param request rq
* @return map
* @throws Exception EX
*/
Map<String, String> fetchCaptcha(HttpServletRequest request) throws Exception;
}
package cn.quantgroup.xyqb.service.captcha.qg;
import cn.quantgroup.xyqb.Constants;
import cn.quantgroup.xyqb.thirdparty.jcaptcha.AbstractManageableImageCaptchaService;
import org.apache.commons.codec.binary.Base64;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
/**
* @author xufei on 2018/1/30.
*/
@Service
public class QuantgroupCaptchaServiceImpl implements IQuantgroupCaptchaService{
private static final String IMAGE_FORMAT_PNG = "png";
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
@Qualifier("customCaptchaService")
private AbstractManageableImageCaptchaService imageCaptchaService;
@Override
public Map<String, String> fetchCaptcha(HttpServletRequest request) throws Exception{
String imageId = UUID.randomUUID().toString();
BufferedImage challenge = imageCaptchaService.getImageChallengeForID(Constants.IMAGE_CAPTCHA_KEY + imageId, request.getLocale());
ByteArrayOutputStream jpegOutputStream = new ByteArrayOutputStream();
ImageIO.write(challenge, IMAGE_FORMAT_PNG, jpegOutputStream);
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 data;
}
}
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