图片码生成

1.项目结构

2.类的内容

2.1 Digest

package com.rtsm.zhjs.background.common.imgcode;

/**
 * @author loki
 * @date 2018-04-27 上午10:16
 **/

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.security.MessageDigest;

public class Digest {
    private static final Logger logger = LoggerFactory.getLogger(Digest.class);
    protected String algorithm;

    public Digest(String alg) {
        this.algorithm = alg;
    }

    public byte[] encode2bytes(byte[] bytes) {
        try {
            MessageDigest md = MessageDigest.getInstance(this.algorithm);
            return md.digest(bytes);
        } catch (Exception var3) {
            logger.error("MessageDigest with {} fail", this.algorithm);
            logger.error("MessageDigest fail", var3);
            return null;
        }
    }

    public byte[] encode2bytes(String data, String charset) {
        if (data == null) {
            return null;
        } else {
            try {
                byte[] bytes;
                if (charset == null) {
                    bytes = data.getBytes();
                } else {
                    bytes = data.getBytes(charset);
                }

                return this.encode2bytes(bytes);
            } catch (Exception var4) {
                logger.error("MD5 encode fail", var4);
                return null;
            }
        }
    }

    public String encode(String data, String charset, boolean lowercase) {
        byte[] endata = this.encode2bytes(data, charset);
        return endata == null ? null : HexCode.encode(endata, lowercase);
    }

    public String encode(String data, boolean lowercase) {
        return this.encode(data, (String) null, lowercase);
    }

    public String encode(String data) {
        return this.encode(data, (String) null, true);
    }

    public String encode(String data, String charset) {
        return this.encode(data, charset, true);
    }

}

2.2 HexCode

package com.rtsm.zhjs.background.common.imgcode;

/**
 * @author loki
 * @date 2018-04-27 上午10:18
 **/
public class HexCode {


    private static final char[] UPPER_HEX_CHARS = new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
    private static final char[] LOWER_HEX_CHARS = new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};

    public HexCode() {
    }

    public static char[] encode2char(byte[] bytes, boolean lowercase) {
        char[] chars = new char[bytes.length * 2];

        for (int i = 0; i < chars.length; i += 2) {
            byte b = bytes[i / 2];
            char[] HEX_CHARS = LOWER_HEX_CHARS;
            if (!lowercase) {
                HEX_CHARS = UPPER_HEX_CHARS;
            }

            chars[i] = HEX_CHARS[b >>> 4 & 15];
            chars[i + 1] = HEX_CHARS[b & 15];
        }

        return chars;
    }

    public static String encode(byte[] bytes, boolean lowercase) {
        char[] endata = encode2char(bytes, lowercase);
        return new String(endata);
    }

    public static byte[] decode(String data) {
        int len = (data.length() + 1) / 2;
        byte[] bytes = new byte[len];
        int index = 0;

        for (int i = 1; i < data.length(); i += 2) {
            char h = data.charAt(i - 1);
            char l = data.charAt(i);
            bytes[index] = decodeByte(h, l);
            ++index;
        }

        return bytes;
    }

    public static byte decodeByte(char hight, char low) {
        int value;
        byte data;
        if (hight >= 65 && hight <= 70) {
            value = hight - 65 + 10;
            data = (byte) (value << 4);
        } else if (hight >= 97 && hight <= 102) {
            value = hight - 97 + 10;
            data = (byte) (value << 4);
        } else {
            if (hight < 48 || hight > 57) {
                throw new RuntimeException();
            }

            value = hight - 48;
            data = (byte) (value << 4);
        }

        if (low >= 65 && low <= 70) {
            value = low - 65 + 10;
            data |= (byte) value;
        } else if (low >= 97 && low <= 102) {
            value = low - 97 + 10;
            data |= (byte) value;
        } else {
            if (low < 48 || low > 57) {
                throw new RuntimeException();
            }

            value = low - 48;
            data |= (byte) value;
        }

        return data;
    }


}

2.3 ImageCode

package com.rtsm.zhjs.background.common.imgcode;

/**
 * 图形验证码对象
 *
 * @author loki
 * @date 2018-04-27 上午10:12
 **/
public class ImgCode {
    private String key;
    private String code;
    private int width = -1;
    private int height = -1;
    private String type = "png";
    private int expired = 3600;//默认600s

    public ImgCode(String code) {
        this.code = code;
    }

    public ImgCode(String key, String code) {
        super();
        this.key = key;
        this.code = code;
    }

    public String getKey() {
        return key;
    }

    public void setKey(String key) {
        this.key = key;
    }

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    public int getWidth() {
        return width;
    }

    public void setWidth(int width) {
        this.width = width;
    }

    public int getHeight() {
        return height;
    }

    public void setHeight(int higth) {
        this.height = higth;
    }

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    public int getExpired() {
        return expired;
    }

    public void setExpired(int expired) {
        this.expired = expired;
    }
}

2.4 ImgWritter.java

2.4.1 ImageWritter

package com.rtsm.zhjs.background.common.imgcode;

import com.github.bingoohuang.patchca.background.MyCustomBackgroundFactory;
import com.github.bingoohuang.patchca.color.ColorFactory;
import com.github.bingoohuang.patchca.custom.ConfigurableCaptchaService;
import com.github.bingoohuang.patchca.filter.AbstractFilterFactory;
import com.github.bingoohuang.patchca.filter.FilterFactory;
import com.github.bingoohuang.patchca.filter.library.CurvesImageOp;
import com.github.bingoohuang.patchca.filter.library.MarbleImageOp;
import com.github.bingoohuang.patchca.filter.library.RippleImageOp;
import com.github.bingoohuang.patchca.font.RandomFontFactory;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletResponse;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.awt.image.BufferedImageOp;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

/**
 *  图片验证码的绘制处理
 * @author loki
 * @date 下午3:48 2018/5/4
 **/
@Slf4j
public class ImgWritter {
    //验证码是否添加干扰点配置项,不配置默认为true
    public static final String CONF_USE_INTERFERENCE = "true";
    private static final String IMG_WIDTH="160";
    private static final String IMG_HEIGHT="70";
    private static final String IMG_FONT_SIZE="48";
    // 初始化图片验证码的主体类
    private static ConfigurableCaptchaService cs = new ConfigurableCaptchaService();
    private static Random random = new Random();
    static {
        // 设置color的生成器
        cs.setColorFactory(new ColorFactory() {
            @Override
            public Color getColor(int x) {
                int[] c = new int[3];
                int i = random.nextInt(c.length);
                for (int fi = 0; fi < c.length; fi++) {
                    if (fi == i) {
                        c[fi] = random.nextInt(71);
                    } else {
                        c[fi] = random.nextInt(256);
                    }
                }
                return new Color(c[0], c[1], c[2]);
            }
        });

        // 设置图片的绘制器
        cs.setFilterFactory((FilterFactory) new SimpleRippleFilterFactory(cs.getColorFactory()));
        // 设置验证码的背景
        cs.setBackgroundFactory(new MyCustomBackgroundFactory());
        // 获取图片的宽度
        String strWidth =IMG_WIDTH;
        if (StringUtils.isNumeric(strWidth)) {
            cs.setWidth(Integer.parseInt(strWidth));
        }

        // 获取图片的高度
        String strHeigth = IMG_HEIGHT;
        if (StringUtils.isNumeric(strHeigth)) {
            cs.setHeight(Integer.parseInt(strHeigth));
        }

        // 设置图片字体的大小
        String strFontSize = IMG_FONT_SIZE;
        if (StringUtils.isNumeric(strFontSize)) {
            if (cs.getFontFactory() instanceof RandomFontFactory) {
                RandomFontFactory fontFactory = (RandomFontFactory) cs.getFontFactory();
                fontFactory.setMinSize(Integer.parseInt(strFontSize));
                fontFactory.setMaxSize(Integer.parseInt(strFontSize));
            }
        }
    }

    /**
     * 图片验证码的响应头部,设置无缓存模式
     *
     * @param imgCode
     * @param response
     */
    public static void responseHeader(ImgCode imgCode, HttpServletResponse response) {
        response.setContentType("image/" + imgCode.getType());
        response.setHeader("Cache-Control", "no-cache, no-store");
        response.setHeader("Pragma", "no-cache");
        long time = System.currentTimeMillis();
        response.setDateHeader("Last-Modified", time);
        response.setDateHeader("Date", time);
        response.setDateHeader("Expires", time);
    }

    /**
     * 响应回写图片验证码内容
     *
     * @param imgCode
     * @param response
     */
    public static void responseImg(ImgCode imgCode, HttpServletResponse response) {
        try {
            int width = imgCode.getWidth();
            int height = imgCode.getHeight();

            if (width < 0) {
                width = cs.getWidth();
            }

            if (height < 0) {
                height = cs.getHeight();
            }

            BufferedImage bufImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
            // 填充背景
            cs.getBackgroundFactory().fillBackground(bufImage);
            cs.getFontFactory().setWord(imgCode.getCode());
            // 绘制验证码
            cs.getTextRenderer().draw(imgCode.getCode(), bufImage, cs.getFontFactory(),
                    cs.getColorFactory());
            // 进行模糊扭曲处理
            bufImage = cs.getFilterFactory().applyFilters(bufImage);
            Graphics g = bufImage.getGraphics();

            if (!"false".equalsIgnoreCase(CONF_USE_INTERFERENCE)) {
                // 绘制干扰点
                for (int i = 0; i < 3 * height; i++) {
                    g.setColor(cs.getColorFactory().getColor(i));
                    int x = random.nextInt(width);
                    int y = random.nextInt(height);
                    g.drawOval(x, y, 0, 0);
                }
            }

            g.dispose();
            // 响应图片
            ImageIO.write(bufImage, imgCode.getType(), response.getOutputStream());
        } catch (Exception e) {
            log.error("create image code stream fail", e);
        }
    }
}

class SimpleRippleFilterFactory extends AbstractFilterFactory {
    public static final String CONF_USE_RIPPLE = "false";
    public static final String CONF_USE_MARBLE = "false";
    public static final String CONF_USE_CURVES = "false";

    protected List<BufferedImageOp> filters;
    // 波浪扭曲处理
    protected RippleImageOp ripple = new RippleImageOp();
    // 模糊处理
    protected MarbleImageOp marble = new MarbleImageOp();
    // 曲线处理
    protected CurvesImageOp curves = new CurvesImageOp();

    public void setColorFactory(ColorFactory colorFactory) {
        curves.setColorFactory(colorFactory);
    }

    public SimpleRippleFilterFactory(ColorFactory colorFactory) {
        setColorFactory(colorFactory);
    }

    protected List<BufferedImageOp> getPreRippleFilters() {
        List<BufferedImageOp> list = new ArrayList<BufferedImageOp>();
        // 检查图片验证码的模糊扭曲处理是否配置,如果配置了则设置相应的处理方式
        // 验证码以波浪形式变形,默认不配置则为false
        if ("true".equalsIgnoreCase(CONF_USE_RIPPLE)) {
            list.add(ripple);
        }
        // 验证码模糊处理,默认不配置则为false
        if ("true".equalsIgnoreCase(CONF_USE_MARBLE)) {
            list.add(marble);
        }
        // //验证码是否加一条曲线,默认不配置则为false
        if ("true".equalsIgnoreCase(CONF_USE_CURVES)) {
            list.add(curves);
        }

        return list;
    }

    protected List<BufferedImageOp> getPostRippleFilters() {
        return new ArrayList<BufferedImageOp>();
    }

    @Override
    public List<BufferedImageOp> getFilters() {
        if (filters == null) {
            filters = new ArrayList<BufferedImageOp>();
            filters.addAll(getPreRippleFilters());
            filters.addAll(getPostRippleFilters());
        }
        return filters;
    }
}

2.4.2 

SimpleRippleFilterFactory
package com.rtsm.zhjs.background.common.imgcode;

import com.github.bingoohuang.patchca.background.MyCustomBackgroundFactory;
import com.github.bingoohuang.patchca.color.ColorFactory;
import com.github.bingoohuang.patchca.custom.ConfigurableCaptchaService;
import com.github.bingoohuang.patchca.filter.AbstractFilterFactory;
import com.github.bingoohuang.patchca.filter.FilterFactory;
import com.github.bingoohuang.patchca.filter.library.CurvesImageOp;
import com.github.bingoohuang.patchca.filter.library.MarbleImageOp;
import com.github.bingoohuang.patchca.filter.library.RippleImageOp;
import com.github.bingoohuang.patchca.font.RandomFontFactory;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletResponse;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.awt.image.BufferedImageOp;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

/**
 *  图片验证码的绘制处理
 * @author loki
 * @date 下午3:48 2018/5/4
 **/
@Slf4j
public class ImgWritter {
    //验证码是否添加干扰点配置项,不配置默认为true
    public static final String CONF_USE_INTERFERENCE = "true";
    private static final String IMG_WIDTH="160";
    private static final String IMG_HEIGHT="70";
    private static final String IMG_FONT_SIZE="48";
    // 初始化图片验证码的主体类
    private static ConfigurableCaptchaService cs = new ConfigurableCaptchaService();
    private static Random random = new Random();
    static {
        // 设置color的生成器
        cs.setColorFactory(new ColorFactory() {
            @Override
            public Color getColor(int x) {
                int[] c = new int[3];
                int i = random.nextInt(c.length);
                for (int fi = 0; fi < c.length; fi++) {
                    if (fi == i) {
                        c[fi] = random.nextInt(71);
                    } else {
                        c[fi] = random.nextInt(256);
                    }
                }
                return new Color(c[0], c[1], c[2]);
            }
        });

        // 设置图片的绘制器
        cs.setFilterFactory((FilterFactory) new SimpleRippleFilterFactory(cs.getColorFactory()));
        // 设置验证码的背景
        cs.setBackgroundFactory(new MyCustomBackgroundFactory());
        // 获取图片的宽度
        String strWidth =IMG_WIDTH;
        if (StringUtils.isNumeric(strWidth)) {
            cs.setWidth(Integer.parseInt(strWidth));
        }

        // 获取图片的高度
        String strHeigth = IMG_HEIGHT;
        if (StringUtils.isNumeric(strHeigth)) {
            cs.setHeight(Integer.parseInt(strHeigth));
        }

        // 设置图片字体的大小
        String strFontSize = IMG_FONT_SIZE;
        if (StringUtils.isNumeric(strFontSize)) {
            if (cs.getFontFactory() instanceof RandomFontFactory) {
                RandomFontFactory fontFactory = (RandomFontFactory) cs.getFontFactory();
                fontFactory.setMinSize(Integer.parseInt(strFontSize));
                fontFactory.setMaxSize(Integer.parseInt(strFontSize));
            }
        }
    }

    /**
     * 图片验证码的响应头部,设置无缓存模式
     *
     * @param imgCode
     * @param response
     */
    public static void responseHeader(ImgCode imgCode, HttpServletResponse response) {
        response.setContentType("image/" + imgCode.getType());
        response.setHeader("Cache-Control", "no-cache, no-store");
        response.setHeader("Pragma", "no-cache");
        long time = System.currentTimeMillis();
        response.setDateHeader("Last-Modified", time);
        response.setDateHeader("Date", time);
        response.setDateHeader("Expires", time);
    }

    /**
     * 响应回写图片验证码内容
     *
     * @param imgCode
     * @param response
     */
    public static void responseImg(ImgCode imgCode, HttpServletResponse response) {
        try {
            int width = imgCode.getWidth();
            int height = imgCode.getHeight();

            if (width < 0) {
                width = cs.getWidth();
            }

            if (height < 0) {
                height = cs.getHeight();
            }

            BufferedImage bufImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
            // 填充背景
            cs.getBackgroundFactory().fillBackground(bufImage);
            cs.getFontFactory().setWord(imgCode.getCode());
            // 绘制验证码
            cs.getTextRenderer().draw(imgCode.getCode(), bufImage, cs.getFontFactory(),
                    cs.getColorFactory());
            // 进行模糊扭曲处理
            bufImage = cs.getFilterFactory().applyFilters(bufImage);
            Graphics g = bufImage.getGraphics();

            if (!"false".equalsIgnoreCase(CONF_USE_INTERFERENCE)) {
                // 绘制干扰点
                for (int i = 0; i < 3 * height; i++) {
                    g.setColor(cs.getColorFactory().getColor(i));
                    int x = random.nextInt(width);
                    int y = random.nextInt(height);
                    g.drawOval(x, y, 0, 0);
                }
            }

            g.dispose();
            // 响应图片
            ImageIO.write(bufImage, imgCode.getType(), response.getOutputStream());
        } catch (Exception e) {
            log.error("create image code stream fail", e);
        }
    }
}

class SimpleRippleFilterFactory extends AbstractFilterFactory {
    public static final String CONF_USE_RIPPLE = "false";
    public static final String CONF_USE_MARBLE = "false";
    public static final String CONF_USE_CURVES = "false";

    protected List<BufferedImageOp> filters;
    // 波浪扭曲处理
    protected RippleImageOp ripple = new RippleImageOp();
    // 模糊处理
    protected MarbleImageOp marble = new MarbleImageOp();
    // 曲线处理
    protected CurvesImageOp curves = new CurvesImageOp();

    public void setColorFactory(ColorFactory colorFactory) {
        curves.setColorFactory(colorFactory);
    }

    public SimpleRippleFilterFactory(ColorFactory colorFactory) {
        setColorFactory(colorFactory);
    }

    protected List<BufferedImageOp> getPreRippleFilters() {
        List<BufferedImageOp> list = new ArrayList<BufferedImageOp>();
        // 检查图片验证码的模糊扭曲处理是否配置,如果配置了则设置相应的处理方式
        // 验证码以波浪形式变形,默认不配置则为false
        if ("true".equalsIgnoreCase(CONF_USE_RIPPLE)) {
            list.add(ripple);
        }
        // 验证码模糊处理,默认不配置则为false
        if ("true".equalsIgnoreCase(CONF_USE_MARBLE)) {
            list.add(marble);
        }
        // //验证码是否加一条曲线,默认不配置则为false
        if ("true".equalsIgnoreCase(CONF_USE_CURVES)) {
            list.add(curves);
        }

        return list;
    }

    protected List<BufferedImageOp> getPostRippleFilters() {
        return new ArrayList<BufferedImageOp>();
    }

    @Override
    public List<BufferedImageOp> getFilters() {
        if (filters == null) {
            filters = new ArrayList<BufferedImageOp>();
            filters.addAll(getPreRippleFilters());
            filters.addAll(getPostRippleFilters());
        }
        return filters;
    }
}

2.5 Md5

package com.rtsm.zhjs.background.common.imgcode;

/**
 * @author loki
 * @date 2018-04-27 上午10:15
 **/
public class Md5 {
    private static final Digest digest = new Digest("MD5");

    public Md5() {
    }

    public static byte[] encode2bytes(byte[] bytes) {
        return digest.encode2bytes(bytes);
    }

    public static byte[] encode2bytes(String data, String charset) {
        return digest.encode2bytes(data, charset);
    }

    public static String encode(String data, String charset, boolean lowercase) {
        return digest.encode(data, charset, lowercase);
    }

    public static String encode(String data, boolean lowercase) {
        return encode(data, (String) null, lowercase);
    }

    public static String encode(String data) {
        return encode(data, (String) null, true);
    }

    public static String encode(String data, String charset) {
        return encode(data, charset, true);
    }
}

3. ImgCodeService

package com.rtsm.zhjs.background.modules.system.service;


import com.ace.cache.EnableAceCache;
import com.ace.cache.service.IRedisService;
import com.rtsm.zhjs.background.common.imgcode.ImgCode;
import com.rtsm.zhjs.background.common.imgcode.Md5;
import com.rtsm.zhjs.background.util.MapUtils;
import com.rtsm.zhjs.background.util.UUIDUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;

import java.util.Map;

/**
 * @author loki
 * @date 2018-04-27 上午10:09
 **/
@PropertySource({"classpath:application.yml"})
@Component
@EnableAceCache
@Slf4j
public class ImgCodeService {

    // 配置文件未配置时默认使用的验证码列表
    private static String[] DEFAULT_CHARS = {"A", "B", "C", "D", "E", "F", "G", "H", "J", "K",
            "L", "M", "P", "Q", "R", "S", "T", "U", "W", "X", "Y", "2", "3", "4", "5", "6", "7",
            "8", "9", "a", "b", "c", "d", "e", "f", "g", "h", "k", "m", "p", "q", "r", "s", "t",
            "u", "w", "x", "y"};

    private static final String IMG_SIGN_KEY = "1f905d35378e4a5d89aeb126faf8baf9";
    private static final String IMG_CACHE_KEY_PREFFIX = "imgcode_";
    private static final int IMG_CODE_EXPIRED = 36000;//二维码过期时间 默认为10分钟
    private static String[] IMG_CHARS;

    @Autowired
    private IRedisService iRedisService;


    /**
     * 获取验证码字符集列表,这里只生成数字加英文(不区分大小写)
     *
     * @return
     */
    private String[] getImgCodeChars() {
        if (IMG_CHARS != null) {
            return IMG_CHARS;
        }
        return DEFAULT_CHARS;
    }

    /**
     * 生成图片验证码内容
     *
     * @return
     */
    public String generateImgCode(int length) {
        if (length < 0) {
            log.info("图形验证码长度不能为0");
        }
        String[] verifyCodes = getImgCodeChars();

        StringBuilder sb = new StringBuilder(length);
        int rand;
        // Random random = new Random();
        // 随机生成索引获取验证码字符
        for (int i = 0; i < length; i++) {
            // rand = random.nextInt(length);
            rand = Integer.valueOf(String.valueOf(Math.round(Math.random()
                    * (verifyCodes.length - 1))));
            sb.append(verifyCodes[rand]);
        }

        return sb.toString();
    }

    /**
     * 如果key存在则使用同一个key刷新验证码内容
     *
     * @param key
     * @param value
     * @param expired
     * @return
     */
    public boolean refreshImgCode(String key, String value, int expired) {
        if (StringUtils.isNotBlank(key) && StringUtils.isNotBlank(value)) {
            if (iRedisService.exists(key)) {
                String result = iRedisService.set(key, value, expired);
                if (StringUtils.isNotBlank(result) && "OK".equals(result)) {
                    return true;
                }
            }
        }

        return false;
    }

    /**
     * 保存验证码
     *
     * @param key
     * @param value
     * @param expired
     * @return
     */
    public String saveImgCode(String key, String value, int expired) {
        if (StringUtils.isNotBlank(key) && StringUtils.isNotBlank(value)) {
            log.info("保存图形验证码");
            return iRedisService.set(IMG_CACHE_KEY_PREFFIX + key, value, expired);
        }
        return "";
    }

    /**
     * 生成图片验证码
     *
     * @param length
     * @param client_ip
     * @return
     */
    public ImgCode generate(int length, String client_ip, String existKey) {
        return generate(length, client_ip, IMG_CODE_EXPIRED, existKey);
    }

    /**
     * 生成图片验证码
     *
     * @param length
     * @param client_ip
     * @param expired
     * @return
     */
    public ImgCode generate(int length, String client_ip, int expired, String existKey) {
        // 生成验证码内容
        String imgCode = generateImgCode(length);
        long time = System.currentTimeMillis();

        // 拼接验证码缓存保存的内容,除验证码外还保存时间、IP、和签名
        StringBuilder content = new StringBuilder(32);
        content.append("img_code=").append(imgCode).append("&time=").append(time).append("&ip=")
                .append(client_ip);

        String sign = Md5.encode(content.toString() + IMG_SIGN_KEY);
        content.append("&sign=").append(sign);
        String value = content.toString();
        String key = existKey;
        // 如果key存在则刷新,不存在则添加
        if (StringUtils.isBlank(key) || !refreshImgCode(key, value, expired)) {
            // uuid生成key
            key = UUIDUtils.generateUuid();
            // 写入缓存,一定要有过期时间,建议验证码过期时间为10分钟
            if (!"OK".equals(saveImgCode(key, value, expired))) {
                log.info("保存图形验证码失败");
            }
        }

        // 返回
        ImgCode imgInfo = new ImgCode(key, imgCode);
        imgInfo.setExpired(expired);
        return imgInfo;
    }

    /**
     * 检查验证码是否正确
     *
     * @param imgInfo
     * @param client_ip
     * @return
     */
    public boolean checkImgCode(ImgCode imgInfo, String client_ip, boolean isDel) {

        String key = IMG_CACHE_KEY_PREFFIX + imgInfo.getKey();
        log.info("-------------key:" + key);
        // 获取缓存中的验证码
        String strSvcInfo = iRedisService.get(key);
        if (StringUtils.isBlank(strSvcInfo)) {
            log.info("img key[{}] cache value is empty", key);
            return false;
        }
        // 检验签名,先截取sign之前的字段
        int pos = strSvcInfo.lastIndexOf("&sign=");
        if (pos < 0) {
            log.info("img key[{}] cache value is empty", key);
            return false;
        }

        String subStr = strSvcInfo.substring(0, pos);
        String sign = Md5.encode(subStr + IMG_SIGN_KEY);
        if (!sign.equals(strSvcInfo.substring(pos + 6))) {
            log.info("img key[{}] cache value sign is invalid", key);
            return false;
        }

        // 获取缓存的数据,校验验证码是否正确
        Map<String, String> imgSvcInfo = MapUtils.str2Map(strSvcInfo);
        String imgCode = imgSvcInfo.get("img_code");

        if (!imgInfo.getCode().equalsIgnoreCase(imgCode)) {
            log.info("img key[{}] code incorrect.cache code={},request code={}",
                    key, imgCode, imgInfo.getCode());
            return false;
        }

        // 如果ip不为null,则验证ip是否正确,因为正常情况生成验证码和使用验证码应该是同一个IP请求
        if (client_ip != null) {
            String ip = imgSvcInfo.get("ip");
            if (!client_ip.equals(ip)) {
                log.error("img key[{}] ip incorrect", key);
                return false;
            }
        }
        // 验证成功后删除redis缓存的信息
        if (!isDel) {
            return true;
        }

        iRedisService.del(key);

        return true;
    }
}

4.html图片码对应文件

点击二维码的时候刷新

 

5.js对应调用

 //从cookie中获取图片码,将后台请求图片接口的路径放入到html的src中即可,imageKey为后台往cookie中塞入的值
    var imageKey = document.cookie;

6.Controller中的代码

/**
     * 获取图形验证码
     *
     * @author loki
     * @date 11:24 PM 2019/3/20
     **/
    @GetMapping(value = "/imgCode/{imgk}")
    @ResponseBody
    public void getImageCode(HttpServletRequest request, HttpServletResponse response, @PathVariable String imgk) {
        ImgCode imgCode = sysYhxxService.getImgCode(ClientUtil.getClientIp(request), imgk);
        ImgWritter.responseHeader(imgCode, response);
        Cookie cookie = new Cookie("imgk",imgCode.getKey());
        cookie.setMaxAge(imgCode.getExpired());
        cookie.setPath("/");
        response.addCookie(cookie);
        ImgWritter.responseImg(imgCode, response);
    }

图片码生成了以后放入到cookie中了,所以在js中直接src(接口访问路径)就可以显示出图片码样式。

存入redis中的imgk也可以在js中获取。

当页面加载的时候就去Controller中获取图片码,并在js中获取imgk,请求登陆的时候传到Controller中,在从redis中根据key获取到对应的imgCode,校验用户输入的是否正确

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在使用 Thymeleaf 生成条形图片时,我们可以借助后端的 Java 代和前端的 JavaScript 代来实现。下面是一个示例: 1. 首先,确保你的项目中已经引入了 Thymeleaf 和 JavaScript 库 `JsBarcode`。 2. 在后端的 Java 代中,使用 `ZXing` 库来生成条形图片。可以创建一个方法,将条形的值作为参数传递进去,并返回生成图片的 Base64 编字符串。 ```java import org.thymeleaf.util.StringUtils; import com.google.zxing.BarcodeFormat; import com.google.zxing.MultiFormatWriter; import com.google.zxing.common.BitMatrix; import com.google.zxing.common.StringUtils; import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel; public class BarcodeGenerator { public static String generateBarcodeImage(String barcodeValue) { if (StringUtils.isEmpty(barcodeValue)) { return null; } try { // 设置条形参数 BarcodeFormat barcodeFormat = BarcodeFormat.CODE_128; int width = 300; int height = 100; // 生成 BitMatrix 对象 BitMatrix bitMatrix = new MultiFormatWriter().encode(barcodeValue, barcodeFormat, width, height); // 将 BitMatrix 转换为 BufferedImage BufferedImage bufferedImage = MatrixToImageWriter.toBufferedImage(bitMatrix); // 将 BufferedImage 转换为 Base64 编字符串 ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); ImageIO.write(bufferedImage, "png", outputStream); byte[] imageBytes = outputStream.toByteArray(); String base64Image = Base64.getEncoder().encodeToString(imageBytes); return base64Image; } catch (Exception e) { e.printStackTrace(); return null; } } } ``` 3. 在前端的 Thymeleaf 模板中,使用 JavaScript 代来调用 `JsBarcode` 并显示生成的条形图片。 ```html <!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <head> <title>生成条形图片</title> <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/jsbarcode.all.min.js"></script> </head> <body> <h1>生成条形图片</h1> <div id="barcode"></div> <script th:inline="javascript"> // 获取需要生成条形的值 var barcodeValue = /*[[${barcodeValue}]]*/ ""; // 使用 Thymeleaf 表达式获取后端生成的条形图片的 Base64 编字符串 var barcodeImage = /*[[${barcodeImage}]]*/ ""; // 在前端使用 JsBarcode 生成条形 JsBarcode("#barcode", barcodeValue, { format: "CODE128", displayValue: true, backgroundImage: "data:image/png;base64," + barcodeImage }); </script> </body> </html> ``` 在上述示例中,我们通过 Thymeleaf 表达式将条形的值传递到前端,并将后端生成的条形图片的 Base64 编字符串通过 Thymeleaf 表达式注入到 JavaScript 代中。然后,我们使用 `JsBarcode` 生成条形,并将生成的条形图片作为背景图片应用到 `<div>` 元素中。 希望这个示例对你有所帮助!如有更多问题,请继续提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值