1.配置Redis
配置参考:005.整合Spring session和Redis
2.添加Jcaptcha依赖
com.octo.captcha
jcaptcha
1.0
3.spring-captcha.xml
${captcha.color.range.red.min}
${captcha.color.range.red.max}
${captcha.color.range.green.min}
${captcha.color.range.green.max}
${captcha.color.range.blue.min}
${captcha.color.range.blue.max}
${captcha.line.color.range.red.min}
${captcha.line.color.range.red.max}
${captcha.line.color.range.green.min}
${captcha.line.color.range.green.max}
${captcha.line.color.range.blue.min}
${captcha.line.color.range.blue.max}
captcha.properties
# CaptchaService设置
# --Captcha session过期时间,单位秒
captcha.service.minGuarantedStorageDelayInSeconds=60
# --最大并发数
captcha.service.maxCaptchaStoreSize=100
captcha.service.captchaStoreLoadBeforeGarbageCollection=100
# 字体设置,字体style、size,size最大最小值
captcha.font.style=1
captcha.font.size=24
captcha.font.minSize=24
captcha.font.maxSize=28
# 验证码文本,生成文本选择的字符,最小和最大文本长度
captcha.word.acceptedChars=1234567890
captcha.word.minLength=4
captcha.word.maxLength=4
# 背景生成,背景图片宽度和高度以及背景颜色
captcha.bg.width=150
captcha.bg.height=42
captcha.bg.color=WHITE
# 干扰线,每个字符干扰线数量
captcha.line.numberOfLinesPerGlyph=1
captcha.baffle.numberOfHolesPerGlyph=1
# 颜色rgb Range
captcha.color.range.red.min=0
captcha.color.range.red.max=150
captcha.color.range.green.min=0
captcha.color.range.green.max=150
captcha.color.range.blue.min=0
captcha.color.range.blue.max=150
captcha.line.color.range.red.min=180
captcha.line.color.range.red.max=255
captcha.line.color.range.green.min=180
captcha.line.color.range.green.max=255
captcha.line.color.range.blue.min=180
captcha.line.color.range.blue.max=255
4.验证码存储器Redis实现
package com.airkisser.familyFinance.utils.jcaptcha;
import com.octo.captcha.Captcha;
import com.octo.captcha.service.CaptchaServiceException;
import com.octo.captcha.service.captchastore.CaptchaAndLocale;
import com.octo.captcha.service.captchastore.CaptchaStore;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import java.io.Serializable;
import java.util.Collection;
import java.util.Locale;
/**
* 基于Redis管理的CaptchaStore
*
* @author luojun at 2017/8/12,QQ: 1146874762
*/
public class AirRedisCaptchaStore implements CaptchaStore {
private static final Logger CAPTCHA_LOG = LoggerFactory.getLogger(AirRedisCaptchaStore.class);
private static final String CAPTCHA_SESSION_KEY = "com:airkisser:captcha";
private RedisTemplate redisTemplate;
private HashOperations hashOperations;
@Override
public boolean hasCaptcha(String sid) {
return this.hashOperations.hasKey(CAPTCHA_SESSION_KEY, sid);
}
@Override
public void storeCaptcha(String sid, Captcha captcha) throws CaptchaServiceException {
CAPTCHA_LOG.debug("Store captcha, Sid: {}.",sid);
this.hashOperations.put(CAPTCHA_SESSION_KEY, sid, captcha);
}
@Override
public void storeCaptcha(String sid, Captcha captcha, Locale locale) throws CaptchaServiceException {
CAPTCHA_LOG.debug("Store captcha, Sid: {}.",sid);
this.hashOperations.put(CAPTCHA_SESSION_KEY, sid, new CaptchaAndLocale(captcha, locale));
}
@Override
public boolean removeCaptcha(String sid) {
if (this.hasCaptcha(sid)) {
CAPTCHA_LOG.debug("Remove captcha, Sid: {}.",sid);
this.hashOperations.delete(CAPTCHA_SESSION_KEY, sid);
return true;
} else {
return false;
}
}
@Override
public Captcha getCaptcha(String sid) throws CaptchaServiceException {
Object val = this.hashOperations.get(CAPTCHA_SESSION_KEY, sid);
if (val == null) return null;
if (val instanceof Captcha) return (Captcha) val;
if (val instanceof CaptchaAndLocale) return ((CaptchaAndLocale) val).getCaptcha();
return null;
}
@Override
public Locale getLocale(String sid) throws CaptchaServiceException {
Object captchaAndLocale = this.getCaptcha(sid);
if (captchaAndLocale != null && captchaAndLocale instanceof CaptchaAndLocale) {
return ((CaptchaAndLocale) captchaAndLocale).getLocale();
}
return null;
}
@Override
public int getSize() {
CAPTCHA_LOG.debug("Get captcha size.");
return Math.toIntExact(this.hashOperations.size(CAPTCHA_SESSION_KEY));
}
@Override
public Collection getKeys() {
CAPTCHA_LOG.debug("Get captcha keys.");
return this.hashOperations.keys(CAPTCHA_SESSION_KEY);
}
@Override
@SuppressWarnings("unchecked")
public void empty() {
Collection keys = this.getKeys();
if (!keys.isEmpty()) {
CAPTCHA_LOG.debug("Empty captcha.");
this.hashOperations.delete(CAPTCHA_SESSION_KEY, keys.toArray());
}
}
@Override
public void initAndStart() {
CAPTCHA_LOG.debug("InitAndStart captcha.");
this.empty();
}
@Override
public void cleanAndShutdown() {
CAPTCHA_LOG.debug("CleanAndShutdown captcha.");
this.empty();
}
@SuppressWarnings("unchecked")
public void setRedisTemplate(RedisTemplate redisTemplate) {
this.redisTemplate = redisTemplate;
this.hashOperations = this.redisTemplate.opsForHash();
}
}
5.每次验证验证码以后,无论成功与否,验证码服务默认会删除生成的验证信息,此处重写验证逻辑,修改为仅验证通过以后才删除验证信息
package com.airkisser.familyFinance.utils.jcaptcha;
import com.octo.captcha.Captcha;
import com.octo.captcha.engine.CaptchaEngine;
import com.octo.captcha.service.CaptchaServiceException;
import com.octo.captcha.service.captchastore.CaptchaStore;
import com.octo.captcha.service.multitype.GenericManageableCaptchaService;
import java.util.Locale;
/**
* GenericManageableCaptchaService扩展
*
* @author luojun at 2017/8/12,QQ: 1146874762
*/
public class AirManageableCaptchaService extends GenericManageableCaptchaService {
public AirManageableCaptchaService(CaptchaStore captchaStore, CaptchaEngine captchaEngine, int minGuarantedStorageDelayInSeconds, int maxCaptchaStoreSize, int captchaStoreLoadBeforeGarbageCollection) {
super(captchaStore, captchaEngine, minGuarantedStorageDelayInSeconds, maxCaptchaStoreSize, captchaStoreLoadBeforeGarbageCollection);
}
/**
* 重写validateResponseForID方法,默认每次经过该操作都会删除对应的Captcha
*
* @param ID SessionID
* @param response 提交的验证码参数值
*/
public Boolean validateResponseForID(String ID, Object response) throws CaptchaServiceException {
if (!this.store.hasCaptcha(ID)) {
throw new CaptchaServiceException("Invalid ID, could not validate unexisting or already validated captcha");
}
Boolean valid = this.store.getCaptcha(ID).validateResponse(response);
if (valid) {//如果验证成功,移除Captcha
this.store.removeCaptcha(ID);
}
return valid;
}
/**
* 每次调用都重新生成
* @param ID
* @param locale
* @return
* @throws CaptchaServiceException
*/
public Object getChallengeForID(String ID, Locale locale) throws CaptchaServiceException {
Captcha captcha;
if(!this.store.hasCaptcha(ID)) {
this.store.removeCaptcha(ID);
}
captcha = this.generateAndStoreCaptcha(locale, ID);
if(captcha == null) {
captcha = this.generateAndStoreCaptcha(locale, ID);
} else if(captcha.hasGetChalengeBeenCalled()) {
captcha = this.generateAndStoreCaptcha(locale, ID);
}
Object challenge = this.getChallengeClone(captcha);
captcha.disposeChallenge();
return challenge;
}
}
6.验证码Controller
package com.airkisser.familyFinance.web;
import com.airkisser.familyFinance.core.result.BooleanResult;
import com.airkisser.familyFinance.core.result.Result;
import com.octo.captcha.service.multitype.GenericManageableCaptchaService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
/**
* JCaptcha验证码Controller
*
* @author luojun at 2017/8/11,QQ: 1146874762
*/
@Controller
@RequestMapping("/captcha")
public class CaptchaController {
private static final Logger LOGGER = LoggerFactory.getLogger(CaptchaController.class);
@Autowired
private GenericManageableCaptchaService captchaService;
// 生成验证码
@RequestMapping(method = RequestMethod.GET)
public void genCaptcha(HttpServletRequest request, HttpServletResponse response) throws IOException {
// the output stream to render the captcha image as jpeg into
ByteArrayOutputStream jpegOutputStream = new ByteArrayOutputStream();
// get the session id that will identify the generated captcha.
// the same id must be used to validate the response, the session id is a good candidate!
String captchaId = request.getSession().getId();
LOGGER.info("Gen Captcha Session Id:" + captchaId);
// call the ImageCaptchaService getChallenge method
BufferedImage challenge = captchaService.getImageChallengeForID(captchaId, request.getLocale());
// a jpeg encoder
// JPEGImageEncoder jpegEncoder = JPEGCodec.createJPEGEncoder(jpegOutputStream);
// jpegEncoder.encode(challenge);
// byte[] captchaChallengeAsJpeg = jpegOutputStream.toByteArray();
// flush it in the response
response.setHeader("Cache-Control", "no-store");
response.setHeader("Pragma", "no-cache");
response.setDateHeader("Expires", 0);
response.setContentType("image/jpeg");
OutputStream outputStream = response.getOutputStream();
// outputStream.write(captchaChallengeAsJpeg);
ImageIO.write(challenge, "jpeg", outputStream);
outputStream.flush();
outputStream.close();
}
// 验证验证码
@RequestMapping(value = "/valid")
@ResponseBody
public Result validCaptcha(HttpServletRequest request, String captcha) {
if (StringUtils.isEmpty(captcha)) {
LOGGER.error("验证码不能为空");
return BooleanResult.errorResult("验证码不能为空", null);
}
String captchaId = request.getSession().getId();
LOGGER.info("Valid Captcha Session Id:" + captchaId);
if (!captchaService.validateResponseForID(captchaId, captcha)) {
LOGGER.error("验证码错误");
return BooleanResult.errorResult("验证码错误", null);
}
return BooleanResult.successResult("验证成功", null);
}
}