cn.cliveyuan.tools.web.CaptchaTools Maven / Gradle / Ivy
package cn.cliveyuan.tools.web;
import cn.cliveyuan.tools.common.AssertTools;
import cn.cliveyuan.tools.common.IdTools;
import cn.cliveyuan.tools.common.StringTools;
import cn.cliveyuan.tools.web.bean.Captcha;
import cn.cliveyuan.tools.web.bean.CaptchaCodeResult;
import cn.cliveyuan.tools.web.bean.CaptchaGenerateType;
import cn.cliveyuan.tools.web.bean.CaptchaRequest;
import cn.cliveyuan.tools.web.bean.CaptchaType;
import cn.cliveyuan.tools.web.bean.CaptchaValidate;
import cn.cliveyuan.tools.web.exception.CaptchaException;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.util.Objects;
/**
* 验证码工具类
*
* @author clive
* Created on 2018-07-27
*/
public class CaptchaTools {
private final static Logger logger = LoggerFactory.getLogger(CaptchaTools.class);
public final static String DEFAULT_CAPTCHA_NAME = "CAPTCHA_NAME";
public final static int DEFAULT_CAPTCHA_LENGTH = 5;
public final static int DEFAULT_CAPTCHA_WIDTH = 300;
public final static int DEFAULT_CAPTCHA_HEIGHT = 100;
private CaptchaTools() {
}
/**
* 校验验证码 v2
*
* 验证码正确不抛异常, 错误抛CaptchaException异常
*
* @param captchaValidate captchaValidate
*/
public static boolean validate(CaptchaValidate captchaValidate) {
logger.debug("CaptchaTools.validate: captchaValidate={}", captchaValidate);
try {
AssertTools.notNull(captchaValidate, "captchaValidate is null");
HttpServletRequest request = captchaValidate.getRequest();
CacheManager cacheManager = captchaValidate.getCacheManager();
String cacheNamespace = captchaValidate.getCacheNamespace();
AssertTools.notNull(captchaValidate.getGenerateType(), "generateType is null");
if (CaptchaGenerateType.SESSION.equals(captchaValidate.getGenerateType())) {
AssertTools.notNull(request, "request is null");
} else if (CaptchaGenerateType.CACHE.equals(captchaValidate.getGenerateType())) {
AssertTools.notNull(cacheManager, "cacheManager is null");
AssertTools.notNull(cacheNamespace, "cacheNamespace is null");
}
Captcha inputCaptcha = captchaValidate.getCaptcha();
String captchaName = captchaValidate.getName();
if (Objects.isNull(inputCaptcha)) {
throw CaptchaException.captchaIsNull();
}
if (StringTools.isBlank(inputCaptcha.getId())) {
throw CaptchaException.idIsEmpty();
}
if (StringTools.isBlank(inputCaptcha.getValue())) {
throw CaptchaException.valueIsEmpty();
}
if (StringTools.isBlank(captchaName)) {
captchaName = DEFAULT_CAPTCHA_NAME;
}
inputCaptcha.setValue(inputCaptcha.getValue().toLowerCase());
Captcha captcha = null;
if (CaptchaGenerateType.SESSION.equals(captchaValidate.getGenerateType())) {
captcha = getFromSession(captchaValidate, captchaName);
} else if (CaptchaGenerateType.CACHE.equals(captchaValidate.getGenerateType())) {
captcha = getFromCache(captchaValidate, captchaName);
}
if (Objects.isNull(captcha)) {
throw CaptchaException.parseJsonError();
}
logger.debug("CaptchaTools.validate: 输入的验证码: {}, 生成的验证码: {}", inputCaptcha, captcha);
// 移除验证码
removeCaptcha(captchaValidate, captchaName);
captcha.setValue(captcha.getValue().toLowerCase()); // fix 大小写不匹配问题
if (!Objects.equals(inputCaptcha, captcha)) {
throw CaptchaException.notMatch();
}
} catch (CaptchaException e) {
if (captchaValidate.isThrowExceptionWhenError()) {
throw e;
}
logger.info("CaptchaTools.validate: ERROR code={}, msg={}", e.getCode(), e.getMessage());
return false;
}
logger.debug("CaptchaTools.validate: matched");
return true;
}
/**
* 生成验证码 v2
*
* @param captchaRequest captchaRequest
*/
public static void generate(CaptchaRequest captchaRequest) {
logger.debug("CaptchaTools.generate captchaRequest={}", captchaRequest);
AssertTools.notNull(captchaRequest, "captchaRequest is null");
HttpServletResponse response = captchaRequest.getResponse();
HttpServletRequest request = captchaRequest.getRequest();
CacheManager cacheManager = captchaRequest.getCacheManager();
String cacheNamespace = captchaRequest.getCacheNamespace();
AssertTools.notNull(response, "response is null");
AssertTools.notNull(captchaRequest.getGenerateType(), "generateType is null");
if (CaptchaGenerateType.SESSION.equals(captchaRequest.getGenerateType())) {
AssertTools.notNull(request, "request is null");
} else if (CaptchaGenerateType.CACHE.equals(captchaRequest.getGenerateType())) {
AssertTools.notNull(cacheManager, "cacheManager is null");
AssertTools.notNull(cacheNamespace, "cacheNamespace is null");
}
int length = captchaRequest.getLength();
int width = captchaRequest.getWidth();
int height = captchaRequest.getHeight();
if (length <= 0) {
length = DEFAULT_CAPTCHA_LENGTH;
}
if (width <= 0) {
width = DEFAULT_CAPTCHA_WIDTH;
}
if (height <= 0) {
height = DEFAULT_CAPTCHA_HEIGHT;
}
try {
System.setProperty("java.awt.headless", "true");
response.setHeader("Pragma", "No-cache");
response.setHeader("Cache-Control", "no-cache");
response.setDateHeader("Expires", 0);
response.setContentType("image/jpeg");
// 生成随机字串
CaptchaCodeResult captchaCodeResult = CaptchaCodeResult.builder().build();
switch (captchaRequest.getCaptchaType()) {
case LETTER_NUM:
captchaCodeResult = VerifyCodeTools.generateLetterNumVerifyCode(length);
break;
case MATH_ADD:
captchaCodeResult = VerifyCodeTools.generateMathAddVerifyCode(captchaRequest.getMathAddStart(),
captchaRequest.getMathAddEnd());
break;
}
String id = captchaRequest.getId();
if (StringTools.isBlank(id)) {
id = IdTools.randomShortUUID();
}
Captcha captcha = new Captcha(id, captchaCodeResult.getVerifyContent());
if (CaptchaGenerateType.SESSION.equals(captchaRequest.getGenerateType())) {
saveToSession(captchaRequest, captcha);
} else if (CaptchaGenerateType.CACHE.equals(captchaRequest.getGenerateType())) {
saveToCache(captchaRequest, captcha);
}
// 生成图片
VerifyCodeTools.outputImage(width, height,
response.getOutputStream(), captchaCodeResult.getCaptchaContent(), !CaptchaType.MATH_ADD.equals(captchaRequest.getCaptchaType()));
logger.debug("CaptchaTools.generate: success -> {}", captcha);
} catch (Exception e) {
logger.error("generate error", e);
}
}
private static String getKey(String captchaName, String id) {
return String.format("%s_%s", captchaName, id);
}
private static void saveToSession(CaptchaRequest captchaRequest, Captcha captcha) {
HttpSession session = captchaRequest.getRequest().getSession(true);
String name = captchaRequest.getName();
if (Objects.isNull(name)) {
name = DEFAULT_CAPTCHA_NAME;
}
session.setAttribute(getKey(name, captcha.getId()), JSON.toJSONString(captcha));
}
private static void saveToCache(CaptchaRequest captchaRequest, Captcha captcha) {
CacheManager cacheManager = captchaRequest.getCacheManager();
Cache cache = cacheManager.getCache(captchaRequest.getCacheNamespace());
String name = captchaRequest.getName();
if (Objects.isNull(name)) {
name = DEFAULT_CAPTCHA_NAME;
}
cache.put(getKey(name, captcha.getId()), captcha);
}
private static Captcha getFromSession(CaptchaValidate captchaValidate, String captchaName) {
HttpSession session = captchaValidate.getRequest().getSession(true);
String captchaStr = (String) session.getAttribute(getKey(captchaName, captchaValidate.getCaptcha().getId()));
if (StringTools.isBlank(captchaStr)) {
throw CaptchaException.notInSession();
}
return JSONObject.parseObject(captchaStr, Captcha.class);
}
private static Captcha getFromCache(CaptchaValidate captchaValidate, String captchaName) {
CacheManager cacheManager = captchaValidate.getCacheManager();
Cache cache = cacheManager.getCache(captchaValidate.getCacheNamespace());
Cache.ValueWrapper valueWrapper = cache.get(getKey(captchaName, captchaValidate.getCaptcha().getId()));
if (Objects.nonNull(valueWrapper)) {
return (Captcha) valueWrapper.get();
}
return null;
}
private static void removeCaptcha(CaptchaValidate captchaValidate, String captchaName) {
if (CaptchaGenerateType.SESSION.equals(captchaValidate.getGenerateType())) {
removeFromSession(captchaValidate, captchaName);
} else if (CaptchaGenerateType.CACHE.equals(captchaValidate.getGenerateType())) {
removeFromCache(captchaValidate, captchaName);
}
}
private static void removeFromSession(CaptchaValidate captchaValidate, String captchaName) {
HttpSession session = captchaValidate.getRequest().getSession(true);
session.removeAttribute(getKey(captchaName, captchaValidate.getCaptcha().getId()));
}
private static void removeFromCache(CaptchaValidate captchaValidate, String captchaName) {
CacheManager cacheManager = captchaValidate.getCacheManager();
Cache cache = cacheManager.getCache(captchaValidate.getCacheNamespace());
cache.evict(getKey(captchaName, captchaValidate.getCaptcha().getId()));
}
}