jcaptcha实战

18.4.14

1.1、问题(1):咨询页面没有登录的人也可以发邮件,使得恶意攻击可以随意发几千几万封邮件,所以不得不加了验证码。
解决:网上随便找了几篇文章看了看,对验证码的细节操作还是挺多的,按照其中一篇结合spring配置的很容易就成功了,然后就用它了。
1.2、问题(2):线上服务器有两台,才有负载均衡将请求打到不同的机器,使得A机器产生的验证码,有可能在B机器验证,而导致输入正确验证码却显示错误。因此需要在AB两机器中共享用于判断输入验证码判断的captchaStore存在AB共享的Redis中作为value。接下来就是找合适的key,一个浏览器对AB机器会生成不同的sessionid,AB机器正常可以做session共享的,但是会影响性能而被网络人员拒绝(哎,项目也真是烂),所以只好自己生成UUID,先传给前台隐藏标签,再传给两个设备。此时AB两台设备使用同样的key便可以找到共享的captchaStore。
1.3、问题(3):以上实现方式有个问题,用户反复刷新咨询页面,就会不断生成新的UUID,刷新过多会使得reids里面同一时间存储过多验证码对象。当然这些对象在redis中的存活时间可以设置,验证码一般为2分钟。

2、在maven的仓库中只看到1.0版本的jcaptcha


com.octo.captcha
jcaptcha
1.0

3、前台代码如下:


4、controller里面的
public void getCode(HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse) {
byte[] captchaChallengeAsJpeg = null;
// 输出jpg的字节流
ByteArrayOutputStream jpegOutputStream = new ByteArrayOutputStream();
try {
// String captchaId = httpServletRequest.getSession().getId();
String uuid = httpServletRequest.getParameter("uuid");
BufferedImage challenge = (BufferedImage) captchaService
.getChallengeForID(uuid,
httpServletRequest.getLocale());
// a jpeg encoder
JPEGImageEncoder jpegEncoder = JPEGCodec
.createJPEGEncoder(jpegOutputStream);
jpegEncoder.encode(challenge);
captchaChallengeAsJpeg = jpegOutputStream.toByteArray();
// flush it in the response
httpServletResponse.setHeader("Cache-Control", "no-store");
httpServletResponse.setHeader("Pragma", "no-cache");
httpServletResponse.setDateHeader("Expires", 0);
httpServletResponse.setContentType("image/jpeg");
ServletOutputStream responseOutputStream = httpServletResponse
.getOutputStream();
responseOutputStream.write(captchaChallengeAsJpeg);
responseOutputStream.flush();
responseOutputStream.close();
} catch (Exception e) {
try {
httpServletResponse.sendError(HttpServletResponse.SC_NOT_FOUND);
} catch (IOException e1) {
e1.printStackTrace();
}
return;
}
}
关键代码就一行:BufferedImage challenge = (BufferedImage) captchaService.getChallengeForID(uuid,httpServletRequest.getLocale());
传入的captchaId是uuid。captchaService需要在Spring中配置Bean。

5、spring中配置jcaptcha的Bean


class="com.octo.captcha.service.multitype.GenericManageableCaptchaService">
验证码服务







300


20000


20000



6、验证码配置
public class PortalCaptchaEngine extends ListImageCaptchaEngine {
@Override
protected void buildInitialFactories() {
int minWordLength = 4;
int maxWordLength = 5;
int fontSize = 50;
int imageWidth = 152;
int imageHeight = 100;
WordGenerator wordGenerator = new RandomWordGenerator(
"0123456789abcdefghijklmnopqrstuvwxyz");
TextPaster randomPaster = new DecoratedRandomTextPaster(minWordLength,
maxWordLength, new RandomListColorGenerator(new Color[] {
new Color(23, 170, 27), new Color(220, 34, 11),
new Color(23, 67, 172) }), new TextDecorator[] {});
BackgroundGenerator background = new UniColorBackgroundGenerator(
imageWidth, imageHeight, Color.white);
FontGenerator font = new RandomFontGenerator(fontSize, fontSize,
new Font[] { new Font("nyala", Font.BOLD, fontSize),
new Font("Bell MT", Font.PLAIN, fontSize),
new Font("Credit valley", Font.BOLD, fontSize) });
ImageDeformation postDef = new ImageDeformationByFilters(
new ImageFilter[] {});
ImageDeformation backDef = new ImageDeformationByFilters(
new ImageFilter[] {});
ImageDeformation textDef = new ImageDeformationByFilters(
new ImageFilter[] {});
WordToImage word2image = new DeformedComposedWordToImage(font,
background, randomPaster, backDef, textDef, postDef);
addFactory(new GimpyFactory(wordGenerator, word2image));
}
}

7.扩展captchaStore,使用redis进行存取。
public class SessionCaptchaStoreImpl implements CaptchaStore {
private Set keySet;//用于必须实现的方法getSize()/getKeys
private static final String REDIS_XXXX_XXXXX_KEY = "项目名功能页面名_";//区别其他验证码
private static final Integer REDIS_XXXX_KEY_EXPIRED = 5 * 60; //time unit: second
@Resource
private RedisUtil redisUtil;
public SessionCaptchaStoreImpl() {
this.keySet = new HashSet();
}
@Override
public boolean hasCaptcha(String s) {
Object o = redisUtil.select(REDIS_XXXX_XXXXX_KEY+s);
if (o == null || o.equals("")) {
return false;
}else {
return true;
}
}
@Override
public void storeCaptcha(String s, Captcha captcha) throws CaptchaServiceException {
captcha.getChallenge();
keySet.add(REDIS_XXXX_XXXXX_KEY + s);
if (hasCaptcha(s)) {
redisUtil.delete(REDIS_XXXX_XXXXX_KEY + s);
}
redisUtil.putByTime(REDIS_XXXX_XXXXX_KEY+s,new CaptchaAndLocale(captcha),REDIS_XXXX_KEY_EXPIRED);
}
@Override
public void storeCaptcha(String s, Captcha captcha, Locale locale) throws CaptchaServiceException {
captcha.getChallenge();
keySet.add(REDIS_XXXX_XXXXX_KEY + s);
if (hasCaptcha(s)) {
redisUtil.delete(REDIS_XXXX_XXXXX_KEY + s);
}
redisUtil.putByTime(REDIS_XXXX_XXXXX_KEY+s,new CaptchaAndLocale(captcha,locale),REDIS_XXXX_KEY_EXPIRED);
}
@Override
public boolean removeCaptcha(String s) {
if (redisUtil.select(REDIS_XXXX_XXXXX_KEY + s) != null) {
keySet.remove(REDIS_XXXX_XXXXX_KEY + s);
redisUtil.delete(REDIS_XXXX_XXXXX_KEY + s);
return true;
}
return false;
}
@Override
public Captcha getCaptcha(String s) throws CaptchaServiceException {
Object captchaAndLocale = redisUtil.select(REDIS_XXXX_XXXXX_KEY+s);
if (captchaAndLocale == null || captchaAndLocale.equals("")) {
return null;
}else {
return ((CaptchaAndLocale)captchaAndLocale).getCaptcha();
}
}
@Override
public Locale getLocale(String s) throws CaptchaServiceException {
Object captchaAndLocale = redisUtil.select(REDIS_XXXX_XXXXX_KEY+s);
if (captchaAndLocale == null || captchaAndLocale.equals("")) {
return null;
}else {
return ((CaptchaAndLocale)captchaAndLocale).getLocale();
}
}
@Override
public int getSize() {
return keySet.size();
}
@Override
public Collection getKeys() {
return keySet;
}
@Override
public void empty() {
for (Iterator iterator = keySet.iterator(); iterator.hasNext();) {
String key = iterator.next();
keySet.remove(key);
redisUtil.delete(key);
}
}
@Override
public void initAndStart() {
}
@Override
public void cleanAndShutdown() {
}
}

8、验证逻辑
@RequestMapping(value = "/XXXX/getJCaptchaState")
@ResponseBody
public Message getJCaptchaState(HttpServletRequest request,String code){
LOGGER.info("输入的验证码:"+code);
Message msg = Message.success();

    Boolean isCorrect = Boolean.FALSE;
    //String captchaId = request.getSession().getId();
    String uuid = request.getParameter("uuid");
    try {
        isCorrect = captchaService.validateResponseForID(uuid,code);
        if (isCorrect) {
            msg.setData(isCorrect);
            msg.setCode("success");
        } else {
            msg.setData(isCorrect);
            msg.setCode("error");
        }
    } catch (Exception e) {
        LOGGER.error(e.getMessage());
    }
    return msg;
}

9、前端js提交
var captcha = getJCaptchaState($("#uuid").val());
if(!captcha){
layer.alert("验证码错误");
$("img.img").trigger("click");
return;
}
function getJCaptchaState(uuid) {
var flag = false;
var code = {"code":$("#code").val(),"uuid":uuid};
$.ajax({
type : "post",
url : "/XXXX/getJCaptchaState",
data : code,
async : false,
success : function(result){
if(result && result.code=="success"&&result.data&&result.data){
flag = true;
}
},
error:function () {
alert("请求出错,请稍后重试");
return false;
}
});
return flag;
}

你可能感兴趣的:(jcaptcha实战)