006.Spring整合JCaptcha并使用Redis存储验证码

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);
    }


}

你可能感兴趣的:(006.Spring整合JCaptcha并使用Redis存储验证码)