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

给getHeaderParam方法添加失败返回

parent f1bc1f94
...@@ -37,7 +37,9 @@ public interface Constants { ...@@ -37,7 +37,9 @@ public interface Constants {
String REDIS_VOICE_DEVICE_COUNT = "Voice_Device_verification_code_count:"; String REDIS_VOICE_DEVICE_COUNT = "Voice_Device_verification_code_count:";
String REDIS_VERIFICATION_COUNT = "verification_code_count:"; String REDIS_VERIFICATION_COUNT = "verification_code_count:";
final Long Image_Need_Count=3L; final Long Image_Need_Count = 3L;
final Long IMAGE_FINITE_COUNT = 3L;
/** /**
* redis中token的key值前缀 * redis中token的key值前缀
*/ */
......
package cn.quantgroup.xyqb.aspect.captcha;
import java.lang.annotation.*;
/**
* 限次的图形验证码校验标记
* @author 任文超
* @version 1.0.0
* @since 2017-11-07
*/
@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CaptchaFineteValidator {
}
package cn.quantgroup.xyqb.aspect.captcha;
import cn.quantgroup.xyqb.Constants;
import cn.quantgroup.xyqb.model.JsonResult;
import cn.quantgroup.xyqb.thirdparty.jcaptcha.AbstractManageableImageCaptchaService;
import com.octo.captcha.service.CaptchaServiceException;
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;
import javax.servlet.http.HttpServletRequest;
import java.nio.charset.Charset;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
/**
* 限次图形验证码校验标记
* @author 任文超
* @version 1.0.0
* @since 2017-11-07
*/
@Aspect
@Component
public class CaptchaFiniteValidateAdvisor {
private static final Logger LOGGER = LoggerFactory.getLogger(CaptchaFiniteValidateAdvisor.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_CAPTCHA_ID_COUNT = "image:captcha_id:count:";
/**
* 图形验证码错误计数器生命周期,与图形验证码生命周期保持一致
* 参照点:cn.quantgroup.xyqb.config.captcha.RedisCaptchaStore{
* DEFAULT_EXPIRED_IN = 120L;
* DEFAULT_EXPIRED_TIMEUNIT = TimeUnit.SECONDS;
* }
*/
private static final Long SECONDS = 2 * 60L;
@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.CaptchaFineteValidator)")
private void needCaptchaFiniteValidate() {
}
/**
* 在受保护的接口方法执行前, 执行限次图形验证码校验
*
* @param pjp
* @return
* @throws Throwable
*/
@Around("needCaptchaFiniteValidate()")
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");
if (shouldSkipCaptchaValidate(registerFrom, captchaId, captchaValue)) {
LOGGER.info("使用超级图形验证码校验, registerFrom={}, clientIp={}", registerFrom, request.getRemoteAddr());
return pjp.proceed();
}
if (captchaValue != null) {
String captcha = String.valueOf(captchaValue);
// 忽略用户输入的大小写
captcha = StringUtils.lowerCase(captcha);
// 验证码校验
Boolean validCaptcha = false;
Long captchaCount = countCaptchaId(captchaId);
if(captchaCount == null || captchaCount >= Constants.IMAGE_FINITE_COUNT){
return JsonResult.buildSuccessResult("验证码失效,请重新获取", "", 2L);
}
try {
validCaptcha = imageCaptchaService.validateResponseForID(Constants.IMAGE_CAPTCHA_KEY + captchaId, captcha);
} catch (CaptchaServiceException ex) {
LOGGER.error("验证码校验异常, {}, {}", ex.getMessage(), ex);
}
if (validCaptcha) {
return pjp.proceed();
}
}
return JsonResult.buildSuccessResult("图形验证码不正确", "", 2L);
}
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 countCaptchaId(String captchaId) {
String captchaKey = getKey(captchaId);
if(StringUtils.isBlank(captchaKey)){
return null;
}
if (!redisTemplate.hasKey(captchaKey)) {
redisTemplate.opsForValue().set(captchaKey, String.valueOf(0), SECONDS, TimeUnit.SECONDS);
}
return redisTemplate.opsForValue().increment(captchaKey, 1L);
}
private final static String getKey(String captchaId){
if(StringUtils.isBlank(captchaId)){
return null;
}
return IMAGE_CAPTCHA_ID_COUNT + captchaId;
}
}
...@@ -103,19 +103,15 @@ public class CaptchaValidateAdvisor { ...@@ -103,19 +103,15 @@ public class CaptchaValidateAdvisor {
return pjp.proceed(); return pjp.proceed();
} }
} }
return result; return result;
} }
private boolean shouldSkipCaptchaValidate(String registerFrom, String captchaId, Object captchaValue) { private boolean shouldSkipCaptchaValidate(String registerFrom, String captchaId, Object captchaValue) {
// 如果启用了超级验证码功能, 检查超级验证码, 超级验证码区分大小写 // 如果启用了超级验证码功能, 检查超级验证码, 超级验证码区分大小写
if (autoTestCaptchaEnabled) { if (autoTestCaptchaEnabled) {
return true; return true;
} }
return StringUtils.equals(SUPER_CAPTCHA_ID, String.valueOf(captchaId)) && StringUtils.equals(SUPER_CAPTCHA, String.valueOf(captchaValue)); return StringUtils.equals(SUPER_CAPTCHA_ID, String.valueOf(captchaId)) && StringUtils.equals(SUPER_CAPTCHA, String.valueOf(captchaValue));
} }
} }
package cn.quantgroup.xyqb.controller.internal.user; package cn.quantgroup.xyqb.controller.internal.user;
import cn.quantgroup.xyqb.Constants; import cn.quantgroup.xyqb.Constants;
import cn.quantgroup.xyqb.aspect.captcha.CaptchaFineteValidator;
import cn.quantgroup.xyqb.aspect.captcha.CaptchaValidator; import cn.quantgroup.xyqb.aspect.captcha.CaptchaValidator;
import cn.quantgroup.xyqb.aspect.token.OneTimeTokenValidator; import cn.quantgroup.xyqb.aspect.token.OneTimeTokenValidator;
import cn.quantgroup.xyqb.controller.IBaseController; import cn.quantgroup.xyqb.controller.IBaseController;
...@@ -41,7 +42,7 @@ import java.util.Map; ...@@ -41,7 +42,7 @@ import java.util.Map;
import java.util.Random; import java.util.Random;
/** /**
* Created by FrankChow on 15/7/5. * Http服务接口:用户注册、登录、重置密码
*/ */
@RestController @RestController
@RequestMapping("/user") @RequestMapping("/user")
...@@ -78,7 +79,10 @@ public class UserController implements IBaseController { ...@@ -78,7 +79,10 @@ public class UserController implements IBaseController {
/** /**
* 登录(账号 + 密码),H5专用入口 * 登录(账号 + 密码)
* 密码错误达到限定次数时执行图形验证码校验
* 图形验证码累计错误达到限定次数时须重新获取
*
* @param channelId * @param channelId
* @param appChannel * @param appChannel
* @param createdFrom * @param createdFrom
...@@ -91,7 +95,7 @@ public class UserController implements IBaseController { ...@@ -91,7 +95,7 @@ public class UserController implements IBaseController {
*/ */
@CaptchaValidator @CaptchaValidator
@RequestMapping("/loginForH5") @RequestMapping("/loginForH5")
public JsonResult loginForH5( public JsonResult loginWithFineteCaptcha(
@RequestParam(required = false, defaultValue = "1") Long channelId, String appChannel, @RequestParam(required = false, defaultValue = "1") Long channelId, String appChannel,
@RequestParam(required = false, defaultValue = "1") Long createdFrom, @RequestParam(required = false, defaultValue = "1") Long createdFrom,
@RequestParam(required = false, defaultValue = "") String userId, @RequestParam(required = false, defaultValue = "") String userId,
...@@ -103,6 +107,9 @@ public class UserController implements IBaseController { ...@@ -103,6 +107,9 @@ public class UserController implements IBaseController {
/** /**
* 快速登录(手机号 + 短信验证码),H5专用入口 * 快速登录(手机号 + 短信验证码),H5专用入口
* 短信验证码错误达到限定次数时执行图形验证码校验
* 图形验证码累计错误达到限定次数时须重新获取
*
* @param channelId * @param channelId
* @param appChannel * @param appChannel
* @param createdFrom * @param createdFrom
...@@ -114,7 +121,7 @@ public class UserController implements IBaseController { ...@@ -114,7 +121,7 @@ public class UserController implements IBaseController {
*/ */
@OneTimeTokenValidator @OneTimeTokenValidator
@RequestMapping("/login/fastForH5") @RequestMapping("/login/fastForH5")
public JsonResult loginFastForH5( public JsonResult loginFastWithFineteCaptcha(
@RequestParam(required = false, defaultValue = "1") Long channelId, String appChannel, @RequestParam(required = false, defaultValue = "1") Long channelId, String appChannel,
@RequestParam(required = false, defaultValue = "1") Long createdFrom, @RequestParam(required = false, defaultValue = "1") Long createdFrom,
@RequestParam(required = false,defaultValue = "xyqb") String key, @RequestParam(required = false,defaultValue = "xyqb") String key,
...@@ -244,31 +251,41 @@ public class UserController implements IBaseController { ...@@ -244,31 +251,41 @@ public class UserController implements IBaseController {
String credential = request.getHeader("authorization"); String credential = request.getHeader("authorization");
if (StringUtils.isBlank(credential)) { if (StringUtils.isBlank(credential)) {
result.put("fail", JsonResult.buildErrorStateResult("登录失败", null)); result.put("fail", JsonResult.buildErrorStateResult("登录失败", null));
return result;
} }
if (!credential.startsWith(verificationHeader)) { if (!credential.startsWith(verificationHeader)) {
result.put("fail", JsonResult.buildErrorStateResult("登录失败", null)); result.put("fail", JsonResult.buildErrorStateResult("登录失败", null));
return result;
} }
credential = credential.substring(verificationHeader.length(), credential.length()); credential = credential.substring(verificationHeader.length(), credential.length());
byte[] buf = Base64.decodeBase64(credential); byte[] buf = Base64.decodeBase64(credential);
boolean validCharset = true;
try { try {
credential = new String(buf, "UTF-8"); credential = new String(buf, "UTF-8");
} catch (UnsupportedEncodingException e) { } catch (UnsupportedEncodingException e) {
LOGGER.error("不支持的编码."); LOGGER.error("不支持的编码.");
result.put("fail", JsonResult.buildErrorStateResult("登录失败", null)); result.put("fail", JsonResult.buildErrorStateResult("登录失败", null));
} }
if (!validCharset) {
result.put("fail", JsonResult.buildErrorStateResult("登录失败", null));
return result;
}
String[] credentialArr = credential.split(":"); String[] credentialArr = credential.split(":");
if (credentialArr.length != 2) { if (credentialArr.length != 2) {
result.put("fail", JsonResult.buildErrorStateResult("登录失败", null)); result.put("fail", JsonResult.buildErrorStateResult("登录失败", null));
return result;
} }
String phoneNo = credentialArr[0]; String phoneNo = credentialArr[0];
String verificationCode = credentialArr[1]; String verificationCode = credentialArr[1];
LOGGER.info("用户快速登录,phoneNo:{} , verificationCode:{}", phoneNo, verificationCode); LOGGER.info("用户快速登录,phoneNo:{} , verificationCode:{}", phoneNo, verificationCode);
if (!ValidationUtil.validatePhoneNo(phoneNo)) { if (!ValidationUtil.validatePhoneNo(phoneNo)) {
result.put("fail", JsonResult.buildErrorStateResult("登录失败", null)); result.put("fail", JsonResult.buildErrorStateResult("登录失败", null));
return result;
} }
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));
return result;
} }
result.put("success", JsonResult.buildSuccessResult(verificationCode, phoneNo)); result.put("success", JsonResult.buildSuccessResult(verificationCode, phoneNo));
return result; return result;
...@@ -415,7 +432,7 @@ public class UserController implements IBaseController { ...@@ -415,7 +432,7 @@ public class UserController implements IBaseController {
return JsonResult.buildErrorStateResult("密码应为6-12位", null); return JsonResult.buildErrorStateResult("密码应为6-12位", null);
} }
smsValidForRegister(phoneNo, verificationCode); smsValidForRegister(phoneNo, verificationCode);
if (userService.exist(phoneNo)) { if (!userService.exist(phoneNo)) {
LOGGER.info("修改密码失败,该手机号尚未注册, registerFrom:{}, phoneNo:{}", registerFrom, phoneNo); LOGGER.info("修改密码失败,该手机号尚未注册, registerFrom:{}, phoneNo:{}", registerFrom, phoneNo);
return JsonResult.buildErrorStateResult("该手机号尚未注册", null); 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