图片码生成

2023-11-08

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,校验用户输入的是否正确

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

图片码生成 的相关文章

随机推荐

  • 《前端》jQuery-ajax例子

    我的jQuery ajax的笔记 https blog csdn net bellediao article details 104239715 从其他博主那里截取的例子 并附有疑问解答 例1 将接口中的result字段绘制成表格
  • Windows官方Linux子系统C/C++开发环境搭建

    关于利用 Windows 10 适用于 Linux 的 Windows 子系统 WSL 安装 Linux 在 Windows 平台直接运行而不依赖虚拟机并通过 Visual Studio Code 在 Windows 平台直接开发并调试Li
  • 搜索+防抖

    搜索 防抖 1 以每时每刻这个项目为例 很多页面都有搜索功能 而且跳转的时同一个搜索页面 在完成搜索功能后 在返回原页面 需要一个唯一标识来记载 搜索 goSearch this router push path search query
  • 详解Python进程与线程

    一 进程 1 1 进程概念 进程是资源分配的最小单位 程序隔离的边界 CPU的时间片轮转 在不同的时间段切换执行不同的进程 但是切换进程是比较耗时的 就引来了轻量级进程 也就是所谓的线程 一个进程中包括多个线程 代码流 其实也就是进程中同时
  • 如何进行安全性测试?

    1 功能验证 功能验证是采用软件测试当中的黑盒测试方法 对涉及安全的软件功能 如 用户管理模块 权限管理模块 加密系统 认证系统等进行测试 主要验证上述功能是否有效 具体方法可使用黑盒测试方法 2 漏洞扫描 安全漏洞扫描通常都是借助于特定的
  • [Git]记录Git Rebase和PR经验

    1 Git Pull Request 大概说一下 PR这个操作是你作为一个collaborator切出来了一个分支 在上面做了某些操作 可能是协作开发 可能是优化等 在此之后希望owner把你的代码pull到某个分支 比如master 上的
  • SIFT解析(一)建立高斯金字塔

    SIFT Scale Invariant Feature Transform 尺度不变特征转换 在目标识别 图像配准领域具有广泛的应用 下面按照SIFT特征的算法流程对其进行简要介绍对SIFT特征做简要介绍 高斯金字塔是SIFT特征提取的第
  • Write-back,Write-through及write allocate

    此文为转载 如需原始作者要求删除请私信我 计算机的存储系统采用Register Cache Memory和I O的方式来构成存储系统 无疑是一个性能和经济性的妥协的产物 Cache和Memory机制是计算机硬件的基础内容 这里就不再啰嗦 下
  • 【Java笔记+踩坑】SpringBoot基础4——原理篇

    导航 黑马Java笔记 踩坑汇总 JavaSE JavaWeb SSM SpringBoot 瑞吉外卖 SpringCloud SpringCloudAlibaba 黑马旅游 谷粒商城 目录 1 自动配置工作流程 1 1 bean的加载方式
  • collection集合 地址_Java集合简介

    1 概述 Java 集合框架提供了一套性能优良 使用方便的接口和类 位于java util包中 使用集合框架的时直接从java util中导包 Java 集合框架主要包括两种类型的容器 一种是Collection 存储一个元素集合 另一种是
  • PHP环境gettext使用指引

    gettext简介 gettext是一种国际化与本地化系统 在类Unix系统中编写多语言程序时经常被使用 gettext的一个常用的实现版本为GNU gettext 1995年发布 源代码中所有需要多语言支持的 需要翻译的 字符串都修改为使
  • strcpy和memcpy的区别

    strcpy和memcpy都是标准C库函数 它们有下面的特点 strcpy提供了字符串的复制 即strcpy只用于字符串复制 并且它不仅复制字符串内容之外 还会复制字符串的结束符 已知strcpy函数的原型是 char strcpy cha
  • redis数据库学习->html学习->css学习

    作者 芝士小熊饼干 系列专栏 Python 坚持天数 13天 h1 Hello HTML h1
  • python是一种蟒蛇使用的语言_飞凌干货丨初探Python-这条蟒蛇有多长

    原标题 飞凌干货丨初探Python 这条蟒蛇有多长 Python语言是一种被广泛使用的解释型语言 类似于shell语言 相对应的就是C C 这一类的编译型语言 Python语言有着解释型语言普遍的优缺点 优点就是灵活 可移植性高 具有非常优
  • Dynamics 365新特性介绍:在视图中显示图片和提示

    关注本人微信和易信公众号 微软动态CRM专家罗勇 回复242或者20161230可方便获取本文 同时可以在第一间得到我发布的最新的博文信息 follow me 我的网站是 www luoyong me Dynamics CRM 2011及以
  • 【满分】【华为OD机试真题2023B卷 JAVA&JS】矩阵最大值

    华为OD2023 B卷 机试题库全覆盖 刷题指南点这里 矩阵最大值 知识点矩阵数组 时间限制 1s 空间限制 32MB 限定语言 不限 题目描述 给定一个仅包含0和1的N N二维矩阵 请计算二维矩阵的最大值 计算规则如下 1 每行元素按下标
  • pycharm安装教程

    pycharm是一款功能强大的python编辑器 具有跨平台性 鉴于目前最新版pycharm使用教程较少 为了节约大家摸索此IDE的时间 来介绍一下pycharm在windows下是如何安装的 这是PyCharm的下载地址 http www
  • Java验证码图片生成源代码

    Java验证码图片生成源代码 import javax imageio ImageIO import java awt import java awt image BufferedImage import java io import ja
  • 查询统计Ceph上的pg 总数

    本文中的命令的第一版来源于国外的一个博客 后面的版本为我自己修改的版本 查询的命令如下 ceph pg dump awk pg stat col 1 while col up col col 0 9a f 0 9a f match 0 0
  • 图片码生成

    1 项目结构 2 类的内容 2 1 Digest package com rtsm zhjs background common imgcode author loki date 2018 04 27 上午10 16 import org