SpringBoot 滑动验证码 后端jar包运行可用

 

 将图片放入项目resources/original 

抠图放入 resources/templates

 

Controller 代码

    @GetMapping("/verification")
    @ApiOperation("获取滑动验证码")
    public CommonResult<?> getVerification() {
        return CommonResult.success(imageSlideVerification.getSlideCode());
    }

    @GetMapping("/check")
    @ApiOperation("校验验证码")
    public CommonResult<?> check(@RequestParam String imageToken, @RequestParam String x, @RequestParam String y) {
        if (imageSlideVerification.checkSlideCode(imageToken, x, y)) {
            return CommonResult.success("验证成功!");
        }
        return CommonResult.failed("验证失败!");
    }

 

import com.alibaba.fastjson.JSONObject;
import com.dpxdata.backend.admin.vo.SlideVerificationVO;
import com.dpxdata.backend.db.service.RedisService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.imageio.ImageIO;
import javax.imageio.ImageReadParam;
import javax.imageio.ImageReader;
import javax.imageio.stream.ImageInputStream;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.UUID;

/**
 * 
 * add by Lish 2022/04/29 滑动验证码
 */
@Slf4j
@Component
public class ImageSlideVerification {

    /**
     * 源图目录路径
     */
    private static String ORIGIN_CATALOG = "original";

    /**
     * 模板图目录路径
     */
    private static String TEMPLATE_CATALOG = "templates";

    /**
     * 描边图目录路径
     */
    private static String BORDER_CATALOG = "templates";

    /**
     * Image-key
     */
    private static String IMAGE_KEY = "IMAGE:KEY:";

    @Autowired
    private RedisService redisService;

    /**
     * 获取滑动验证码
     *
     * @return
     */
    public SlideVerificationVO getSlideCode() {
        InputStream oIn = null;
        InputStream tIn = null;
        InputStream bIn = null;
        SlideVerificationVO vo = null;
        try {
            //  原图
            final int index = new Random().nextInt(6);
            log.info(index + "");
            File originalFile = new File(index + ".png");
            oIn = ImageSlideVerification.class.getClassLoader().getResourceAsStream("original/" + index + ".png");
            FileUtils.copyInputStreamToFile(oIn, originalFile);
            //  滑块模板图
            File templateFile = new File("template.png");
            tIn = ImageSlideVerification.class.getClassLoader().getResourceAsStream("templates/template.png");
            FileUtils.copyInputStreamToFile(tIn, templateFile);

            //  滑块描边图片
            File borderFile = new File("border.png");
            bIn = ImageSlideVerification.class.getClassLoader().getResourceAsStream("templates/border.png");
            FileUtils.copyInputStreamToFile(bIn, borderFile);


            // 生成随机x、y
            int x = generateX(originalFile, templateFile);
            int y = generateY(originalFile, templateFile);

            // 生成滑块
            String slide = cutSlideImage(originalFile, templateFile, borderFile, x, y);

            // 生成缺块图
            String back = cutBackImage(originalFile, templateFile, borderFile, x, y);

            // 生成token
            String imageToken = UUID.randomUUID().toString().replace("-", "");

            // 放入redis
            Map<String, Integer> cacheMap = new HashMap<>();
            cacheMap.put("x", x);
            cacheMap.put("y", y);
            log.info("验证码Key => " + imageToken);
            redisService.set(imageToken, cacheMap, 2);
            vo = new SlideVerificationVO();
            vo.setImageToken(imageToken);
            vo.setBackImage(back);
            vo.setSlideImage(slide);
            vo.setY(y + "");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (oIn != null) {
                    oIn.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (tIn != null) {
                    tIn.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (bIn != null) {
                    bIn.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return vo;
    }

    /**
     * 验证滑动验证码
     *
     * @param imageToken
     * @return
     */
    public boolean checkSlideCode(String imageToken, String codeX, String codeY) {
        if (!redisService.hasKey(imageToken)) {
            return false;
        }

        final Map<String, Integer> cacheMap = (Map<String, Integer>) redisService.get(imageToken);


        // 获取缓存中的x、y
        int x = cacheMap.get("x");
        int y = cacheMap.get("y");

        // 验证 x
        int errorX = Integer.parseInt(codeX) - x;
        // update by Lish 2022/05/09 滑动验证码,校验范围修改
        if (errorX > 5 || errorX < -5) {
            return false;
        }

        // 验证 y
        int errorY = Integer.parseInt(codeY) - y;
        if (errorY > 1 || errorY < -1) {
            return false;
        }

        // *****验证路径*****
        // *****验证路径*****

        // 验证成功,修改key的值
        redisService.set(imageToken, "T", 2);
        return true;
    }

    /**
     * 获取跟目录
     *
     * @return
     */
    private static String getClassPath() {
        return ImageSlideVerification.class.getClass().getResource("/").getPath();
    }

    /**
     * 获取随机原图图片
     */
    private static File getOriginalFile() {
        StringBuilder builder = new StringBuilder(getClassPath());
        builder.append(ORIGIN_CATALOG);
        builder.append("/");
        builder.append("1");
        builder.append(".png");

        return new File(builder.toString());
    }

    /**
     * 滑块模板图片
     */
    private static File getTemplateFile() {
        StringBuilder builder = new StringBuilder(getClassPath());
        builder.append(TEMPLATE_CATALOG);
        builder.append("/");
        builder.append("template.png");

        return new File(builder.toString());
    }

    /**
     * 获取描边原图图片
     */
    private static File getBorderFile() {
        StringBuilder builder = new StringBuilder(getClassPath());
        builder.append(BORDER_CATALOG);
        builder.append("/");
        builder.append("border.png");

        return new File(builder.toString());
    }

    /**
     * 随机X坐标
     *
     * @param originalFile
     * @param templateFile
     * @return
     */
    private static int generateX(File originalFile, File templateFile) {
        try {
            BufferedImage originalImage = ImageIO.read(originalFile);
            BufferedImage templateImage = ImageIO.read(templateFile);

            //  原图宽度
            int width = originalImage.getWidth();

            //  模板宽度
            int templateImageWidth = templateImage.getWidth();
            Random random = new Random(System.currentTimeMillis());

            return random.nextInt(width - templateImageWidth) % (width - templateImageWidth - templateImageWidth + 1) + templateImageWidth;

        } catch (IOException e) {
            e.printStackTrace();
        }
        return 0;
    }

    /**
     * 随机Y坐标
     *
     * @param originalFile
     * @param templateFile
     * @return
     */
    private static int generateY(File originalFile, File templateFile) {
        try {
            BufferedImage originalImage = ImageIO.read(originalFile);
            BufferedImage templateImage = ImageIO.read(templateFile);
            //  原图高度
            int height = originalImage.getHeight();

            //  抠图模板高度
            int templateImageHeight = templateImage.getHeight();
            Random random = new Random(System.currentTimeMillis());
            if (templateImageHeight - height >= 0) {
                return random.nextInt(10);
            }

            return random.nextInt(height - templateImageHeight) % (height - templateImageHeight - templateImageHeight + 1) + templateImageHeight;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return 0;
    }

    /**
     * 裁剪滑块
     *
     * @param originalFile
     * @param templateFile
     * @param borderFile
     * @param x
     * @param y
     * @return
     */
    private static String cutSlideImage(File originalFile, File templateFile, File borderFile, int x, int y) {
        ImageInputStream originImageInputStream = null;
        ByteArrayOutputStream byteArrayOutputStream = null;
        try {
            BufferedImage templateImage = ImageIO.read(templateFile);
            BufferedImage borderImage = ImageIO.read(borderFile);

            int with = templateImage.getWidth();
            int height = templateImage.getHeight();

            //  创建滑块
            BufferedImage cutoutImage = new BufferedImage(with, height, templateImage.getType());

            // 从原图中获取与滑块一样大小的4边形图片
            ImageReader originalReader = ImageIO.getImageReadersByFormatName("png").next();
            originImageInputStream = ImageIO.createImageInputStream(originalFile);
            //  图片输入流顺序读写
            originalReader.setInput(originImageInputStream, true);

            //  根据坐标生成矩形
            ImageReadParam imageReadParam = originalReader.getDefaultReadParam();
            Rectangle rectangle = new Rectangle(x, y, with, height);
            imageReadParam.setSourceRegion(rectangle);

            // 从原图中裁剪下来的图片
            BufferedImage cutImage = originalReader.read(0, imageReadParam);

            // 获取剪切图片矩阵颜色
            int[][] cutColorArray = getColorArray(cutImage);
            // 获取模板图片矩阵颜色
            int[][] templateColorArray = getColorArray(templateImage);
            // 获取边框图片矩阵颜色
            int[][] borderColorArray = getColorArray(borderImage);

            // 将模板图非透明像素设置到剪切图中
            for (int i = 0; i < templateColorArray.length; i++) {
                for (int j = 0; j < templateColorArray[0].length; j++) {
                    // 滑块模板颜色不为透明时设置剪切图颜色
                    int templateRGB = templateColorArray[i][j];
                    if (templateRGB != 16777215 && templateRGB < 0) {
                        cutoutImage.setRGB(i, j, cutColorArray[i][j]);
                    }
                    // 设置边框颜色
                    int borderRGB = borderColorArray[i][j];
                    if (borderRGB != 16777215 && borderRGB < 0) {
                        int[] colorArray = {(borderColorArray[i][j] & 0xff0000) >> 16,
                                (borderColorArray[i][j] & 0xff00) >> 8,
                                (borderColorArray[i][j] & 0xff)};

                        // 我的边框是黑色所以我这是加
                        colorArray[0] += (cutColorArray[i][j] & 0xff0000) >> 16;
                        colorArray[1] += (cutColorArray[i][j] & 0xff00) >> 8;
                        colorArray[2] += (cutColorArray[i][j] & 0xff);

                        cutoutImage.setRGB(i, j, new Color(colorArray[0] > 255 ? 255 : colorArray[0],
                                colorArray[1] > 255 ? 255 : colorArray[1],
                                colorArray[2] > 255 ? 255 : colorArray[2]).getRGB());
                    }
                }
            }

            //  图片绘图
            int bold = 5;
            Graphics2D graphics = cutoutImage.createGraphics();
            graphics.setBackground(Color.white);

            //  设置抗锯齿属性
            graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            graphics.setStroke(new BasicStroke(bold, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL));
            graphics.drawImage(cutoutImage, 0, 0, null);
            graphics.dispose();

            // 输出图片流
            byteArrayOutputStream = new ByteArrayOutputStream();
            ImageIO.write(cutoutImage, "png", byteArrayOutputStream);
            //  图片转为二进制字符串
            byte[] cutoutImageBytes = byteArrayOutputStream.toByteArray();
            byteArrayOutputStream.reset();
            //  图片加密成base64字符串
            return String.format("data:image/png;base64,%s", cn.hutool.core.codec.Base64.encode(cutoutImageBytes));
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (originImageInputStream != null) {
                    originImageInputStream.close();
                }
                if (byteArrayOutputStream != null) {
                    byteArrayOutputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        return null;
    }

    /**
     * 生成背景图
     *
     * @param originalFile
     * @param templateFile
     * @param borderFile
     * @param x
     * @param y
     * @return
     */
    private static String cutBackImage(File originalFile, File templateFile, File borderFile, int x, int y) {
        ByteArrayOutputStream byteArrayOutputStream = null;
        try {

            BufferedImage originalImage = ImageIO.read(originalFile);
            BufferedImage templateImage = ImageIO.read(templateFile);

            int with = templateImage.getWidth();
            int height = templateImage.getHeight();

            //  根据原图,创建支持alpha通道的rgb图片
            BufferedImage shadeImage = new BufferedImage(originalImage.getWidth(), originalImage.getHeight(), BufferedImage.TYPE_INT_ARGB);

            // 获取原图片矩阵
            int[][] originColorArray = getColorArray(originalImage);

            //  将原图的像素拷贝到遮罩图
            for (int i = 0; i < originColorArray.length; i++) {
                for (int j = 0; j < originColorArray[0].length; j++) {
                    int originalRGB = originalImage.getRGB(i, j);

                    // 在模板区域内
                    if (i > x && i < (x + with) && j > y && j < (y + height)) {
                        int templateRGB = templateImage.getRGB(i - x, j - y);
                        //对源文件备份图像(x+i,y+j)坐标点进行透明处理
                        if (templateRGB != 16777215 && templateRGB < 0) {

                            int[] rgb = new int[3];
                            rgb[0] = (originalRGB & 0xff0000) >> 16;
                            rgb[1] = (originalRGB & 0xff00) >> 8;
                            rgb[2] = (originalRGB & 0xff);

                            // 对遮罩透明处理
                            shadeImage.setRGB(i, j, new Color(rgb[0] / 3, rgb[1] / 3, rgb[2] / 3).getRGB());
                            continue;
                        }
                    }

                    //  无透明处理
                    shadeImage.setRGB(i, j, originalRGB);
                }
            }

            // 输出图片流
            byteArrayOutputStream = new ByteArrayOutputStream();
            ImageIO.write(shadeImage, "png", byteArrayOutputStream);
            //  图片转为二进制字符串
            byte[] cutoutImageBytes = byteArrayOutputStream.toByteArray();
            byteArrayOutputStream.reset();
            //  图片加密成base64字符串
            return String.format("data:image/png;base64,%s", cn.hutool.core.codec.Base64.encode(cutoutImageBytes));
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (byteArrayOutputStream != null) {
                    byteArrayOutputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        return null;
    }

    /**
     * 图片生成图像矩阵
     *
     * @param bufferedImage 图片源
     * @return 图片矩阵
     */
    private static int[][] getColorArray(BufferedImage bufferedImage) {
        int[][] matrix = new int[bufferedImage.getWidth()][bufferedImage.getHeight()];
        for (int i = 0; i < bufferedImage.getWidth(); i++) {
            for (int j = 0; j < bufferedImage.getHeight(); j++) {
                matrix[i][j] = bufferedImage.getRGB(i, j);
            }
        }
        return matrix;
    }

    public static void main(String[] args) {
        for (int i = 0; i < 50; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    //  原图
                    File originalFile = getOriginalFile();
                    //  模板图
                    File templateFile = getTemplateFile();
                    //  描边图片
                    File borderFile = getBorderFile();

                    int x = generateX(originalFile, templateFile);
                    int y = generateY(originalFile, templateFile);

                    String slide = cutSlideImage(originalFile, templateFile, borderFile, x, y);
                    String back = cutBackImage(originalFile, templateFile, borderFile, x, y);

                    String imageToken = UUID.randomUUID().toString().replace("-", "");
                    SlideVerificationVO vo = new SlideVerificationVO();
                    vo.setImageToken(imageToken);
                    vo.setBackImage(back);
                    vo.setSlideImage(slide);
                    vo.setY(y + "");

                    System.out.println(JSONObject.toJSONString(vo));
                }
            }).start();
        }
    }

}

 vo对象

import lombok.Data;

@Data
public class SlideVerificationVO {
    private String imageToken;
    private String backImage;
    private String slideImage;
    private String x;
    private String y;
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值