滑动图片验证登录-java后端

1、生成前端小图片,以及对于大图片加背景颜色 

package com.stock.core.util;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import sun.misc.BASE64Encoder;

/**
 * 随机切割图片工具类
 *
 * @Author: liuqiaoyue
 * @Date: 2022-05-13 13:32
 * @Version 1.0
 */
public class RandomCutPicUtils {

    /**
     * 背景图的宽、高
     */
    private static final int BG_WIDTH = 590;
    private static final int BG_HEIGHT = 360;

    /**
     * 模板图的宽、高
     */
    private static final int TEMPLATE_WIDTH = 120;
    private static final int TEMPLATE_HEIGHT = 100;

    /**
     * 圆的半径
     */
    private static final int CIRCLE_RADIO = 20;

    /**
     * 抠图的边框宽度
     */
    private static int SLIDER_IMG_OUT_PADDING = 1;

    // --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- //
    /**
     * 获取大图,小图
     * @param bgPathInputStream 背景图文件
     * @return
     * @throws Exception
     */
    public static Map<String, Object> generateTwoImage(InputStream bgPathInputStream) throws Exception {
//   public static Map<String, Object> generateTwoImage(HttpSession session, HttpServletResponse response,  File bgPath) throws Exception {

        Map<String, Integer> startPoint = getStartPoint();
        int xBegin = startPoint.get("xBegin");
        int yBegin = startPoint.get("yBegin");

        // 抠图轮廓
        int[][] templateImage = getBlockData();
        // 原始背景图
        BufferedImage bgImage = ImageIO.read(bgPathInputStream);
        // 抠图(空白)
        BufferedImage blankImage = new BufferedImage(TEMPLATE_WIDTH, TEMPLATE_HEIGHT,
                BufferedImage.TYPE_4BYTE_ABGR);
        Graphics2D graphics = blankImage.createGraphics();
        graphics.setBackground(Color.white);

        cutByTemplate(bgImage, blankImage, templateImage, xBegin, yBegin);

        // “抗锯齿”的属性
        graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        graphics.setStroke(new BasicStroke(5, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL));
        graphics.drawImage(blankImage, 0, 0, null);
        graphics.dispose();

        Map<String, Object> resultMap = new HashMap<>();
        resultMap.put("image", getImageBASE64(bgImage));//大图
        resultMap.put("image2", getImageBASE64(blankImage)); //小图
        resultMap.put("xBegin",xBegin);
        resultMap.put("yBegin", yBegin);
        return resultMap;
    }

    /**
     * 获取大图,小图Base64码
     *
     * @param bgPath 背景图路径
     */
    public static Map<String, String> generateTwoImageBase64(String bgPath)
            throws Exception {

        Map<String, Integer> startPoint = getStartPoint();
        int xBegin = startPoint.get("xBegin");
        int yBegin = startPoint.get("yBegin");

        // 抠图轮廓
        int[][] templateImage = getBlockData();
        // 原始背景图
        BufferedImage bgImage = ImageIO.read(new FileInputStream(bgPath));
        // 空白抠图
        BufferedImage blankImage = new BufferedImage(TEMPLATE_WIDTH, TEMPLATE_HEIGHT,
                BufferedImage.TYPE_4BYTE_ABGR);

        cutByTemplate(bgImage, blankImage, templateImage, xBegin, yBegin);

        Map<String, String> resultMap = new HashMap<>(2);
        // 大图
        resultMap.put("big", getImageBASE64(bgImage));

        // 小图
        resultMap.put("small", getImageBASE64(blankImage));

        return resultMap;
    }

    /**
     * 获取切图在背景图中的 开始点
     */
    private static Map<String, Integer> getStartPoint() {
        Random random = new Random();
        int xBegin = random.nextInt(BG_WIDTH - 3*TEMPLATE_WIDTH) + 2*TEMPLATE_WIDTH + 5;
        int yBegin = TEMPLATE_HEIGHT + random.nextInt(BG_HEIGHT - TEMPLATE_HEIGHT * 2);
        Map<String, Integer> map = new HashMap<>(2);
        map.put("xBegin", xBegin);
        map.put("yBegin", yBegin);
        return map;
    }

    /**
     * 生成随机滑块形状
     * <p>
     * 0 透明像素
     * 1 滑块像素
     * 2 阴影像素
     *
     * @return int[][]
     */
    private static int[][] getBlockData() {
        int[][] data = new int[TEMPLATE_WIDTH][TEMPLATE_HEIGHT];
        Random random = new Random();
        //(x-a)²+(y-b)²=r²
        //x中心位置左右5像素随机
        double x1 = CIRCLE_RADIO + (TEMPLATE_WIDTH - 2 * CIRCLE_RADIO) / 2.0 - 5 + random.nextInt(10);
        //y 矩形上边界半径-1像素移动
        double y1_top = CIRCLE_RADIO - random.nextInt(3);
        double y1_bottom = TEMPLATE_HEIGHT - CIRCLE_RADIO + random.nextInt(3);
        double y1 = random.nextInt(2) == 1 ? y1_top : y1_bottom;


        double x2_right = TEMPLATE_WIDTH - CIRCLE_RADIO - CIRCLE_RADIO + random.nextInt(2 * CIRCLE_RADIO - 4);
        double x2_left = CIRCLE_RADIO + CIRCLE_RADIO - 2 - random.nextInt(2 * CIRCLE_RADIO - 4);
        double x2 = random.nextInt(2) == 1 ? x2_right : x2_left;
        double y2 = CIRCLE_RADIO + (TEMPLATE_HEIGHT - 2 * CIRCLE_RADIO) / 2.0 - 4 + random.nextInt(10);

        double po = Math.pow(CIRCLE_RADIO, 2);
        for (int i = 0; i < TEMPLATE_WIDTH; i++) {
            for (int j = 0; j < TEMPLATE_HEIGHT; j++) {
                //矩形区域
                boolean fill;
                if ((i >= CIRCLE_RADIO && i < TEMPLATE_WIDTH - CIRCLE_RADIO)
                        && (j >= CIRCLE_RADIO && j < TEMPLATE_HEIGHT - CIRCLE_RADIO)) {
                    data[i][j] = 1;
                    fill = true;
                } else {
                    data[i][j] = 0;
                    fill = false;
                }
                //凸出区域
                double d3 = Math.pow(i - x1, 2) + Math.pow(j - y1, 2);
                if (d3 < po) {
                    data[i][j] = 1;
                } else {
                    if (!fill) {
                        data[i][j] = 0;
                    }
                }
                //凹进区域
                double d4 = Math.pow(i - x2, 2) + Math.pow(j - y2, 2);
                if (d4 < po) {
                    data[i][j] = 0;
                }
            }
        }
        //边界阴影
        for (int i = 0; i < TEMPLATE_WIDTH; i++) {
            for (int j = 0; j < TEMPLATE_HEIGHT; j++) {
                //四个正方形边角处理
                for (int k = 1; k <= SLIDER_IMG_OUT_PADDING; k++) {
                    //左上、右上
                    if (i >= CIRCLE_RADIO - k && i < CIRCLE_RADIO
                            && ((j >= CIRCLE_RADIO - k && j < CIRCLE_RADIO)
                            || (j >= TEMPLATE_HEIGHT - CIRCLE_RADIO - k && j < TEMPLATE_HEIGHT - CIRCLE_RADIO + 1))) {
                        data[i][j] = 2;
                    }

                    //左下、右下
                    if (i >= TEMPLATE_WIDTH - CIRCLE_RADIO + k - 1 && i < TEMPLATE_WIDTH - CIRCLE_RADIO + 1) {
                        for (int n = 1; n <= SLIDER_IMG_OUT_PADDING; n++) {
                            if (((j >= CIRCLE_RADIO - n && j < CIRCLE_RADIO)
                                    || (j >= TEMPLATE_HEIGHT - CIRCLE_RADIO - n && j <= TEMPLATE_HEIGHT - CIRCLE_RADIO))) {
                                data[i][j] = 2;
                            }
                        }
                    }
                }

                if (data[i][j] == 1 && j - SLIDER_IMG_OUT_PADDING > 0 && data[i][j - SLIDER_IMG_OUT_PADDING] == 0) {
                    data[i][j - SLIDER_IMG_OUT_PADDING] = 2;
                }
                //解决凸出圆弧没有上/下边框问题
                if (data[i][j] == 1 && j + SLIDER_IMG_OUT_PADDING > 0 && j + SLIDER_IMG_OUT_PADDING <= TEMPLATE_HEIGHT) {
                    if (j == 0 || (j+1) == TEMPLATE_HEIGHT){
                        data[i][j] = 2;
                    }else {
                        if (j + SLIDER_IMG_OUT_PADDING < TEMPLATE_HEIGHT && data[i][j + SLIDER_IMG_OUT_PADDING] == 0) {
                            data[i][j + SLIDER_IMG_OUT_PADDING] = 2;
                        }
                    }
                }
                if (data[i][j] == 1 && i - SLIDER_IMG_OUT_PADDING > 0 && data[i - SLIDER_IMG_OUT_PADDING][j] == 0) {
                    data[i - SLIDER_IMG_OUT_PADDING][j] = 2;
                }
                if (data[i][j] == 1 && i + SLIDER_IMG_OUT_PADDING > 0 && i + SLIDER_IMG_OUT_PADDING < TEMPLATE_WIDTH && data[i + SLIDER_IMG_OUT_PADDING][j] == 0) {
                    data[i + SLIDER_IMG_OUT_PADDING][j] = 2;
                }
            }
        }
        return data;
    }

    /**
     * 生成小图片、给大图片添加阴影
     *
     * @param bgImage       背景图
     * @param blankImage    抠图(空白)
     * @param templateImage 抠图模板
     * @param x             抠图开始坐标
     * @param y             抠图开始坐标
     */
    private static void cutByTemplate(BufferedImage bgImage, BufferedImage blankImage,
                                      int[][] templateImage, int x, int y) {

        for (int i = 0; i < TEMPLATE_WIDTH; i++) {
            for (int j = 0; j < TEMPLATE_HEIGHT; j++) {
                int _x = x + i;
                int _y = y + j;
                int rgbFlg = templateImage[i][j];
                int rgb_ori = bgImage.getRGB(_x, _y);
                // 原图中对应位置变色处理
                if (rgbFlg == 1) {
                    //抠图上复制对应颜色值
                    blankImage.setRGB(i, j, rgb_ori);
                    //原图对应位置颜色变化
                    bgImage.setRGB(_x, _y, rgb_ori & 0x363636);
                } else if (rgbFlg == 2) {
                    blankImage.setRGB(i, j, Color.WHITE.getRGB());
                    bgImage.setRGB(_x, _y, Color.GRAY.getRGB());
                } else if (rgbFlg == 0) {
                    //int alpha = 0;
                    blankImage.setRGB(i, j, rgb_ori & 0x00ffffff);
                }
            }
        }
    }


    /**
     * 获取大图,小图
     *
     * @param bgPath 背景图路径
     */
    public static Map<String, BufferedImage> generateTwoImage(HttpSession session, HttpServletResponse response, String bgPath) throws Exception {

        Map<String, Integer> startPoint = getStartPoint();
        int xBegin = startPoint.get("xBegin");
        int yBegin = startPoint.get("yBegin");

        // 抠图轮廓
        int[][] templateImage = getBlockData();
        // 原始背景图
        BufferedImage bgImage = ImageIO.read(new FileInputStream(bgPath));
        // 抠图(空白)
        BufferedImage blankImage = new BufferedImage(TEMPLATE_WIDTH, TEMPLATE_HEIGHT,
                BufferedImage.TYPE_4BYTE_ABGR);

        cutByTemplate(bgImage, blankImage, templateImage, xBegin, yBegin);

        Map<String, BufferedImage> resultMap = new HashMap<>(2);
        //大图
        resultMap.put("big", bgImage);

        //小图
        resultMap.put("small", blankImage);

        return resultMap;
    }


    /**
     * 图片转BASE64
     */
    private static String getImageBASE64(BufferedImage image) throws IOException {

        ByteArrayOutputStream out = new ByteArrayOutputStream();
        ImageIO.write(image, "png", out);
        //转成byte数组
        byte[] b = out.toByteArray();
        BASE64Encoder encoder = new BASE64Encoder();
        //生成base64编码
        return encoder.encode(b);
    }

    /**
     * 图片文件输出
     *
     * @param image     图
     * @param imagePath 图的输出路径
     */
    public static void outputImageFile(BufferedImage image, String imagePath) throws Exception {

        ByteArrayOutputStream os = new ByteArrayOutputStream();
        ImageIO.write(image, "png", os);
        byte[] newImages = os.toByteArray();
        FileOutputStream fos = new FileOutputStream(imagePath);
        fos.write(newImages);
        fos.close();
    }

    /**
     * 图片文件输出
     *
     * @param image     图
     * @param imagePath 图的输出路径
     */
    public static void outputImageFile(BufferedImage image, File imagePath) throws Exception {

        ByteArrayOutputStream os = new ByteArrayOutputStream();
        ImageIO.write(image, "png", os);
        byte[] newImages = os.toByteArray();
        FileOutputStream fos = new FileOutputStream(imagePath);
        fos.write(newImages);
        fos.close();
    }

}

2、请求地址调用以base64方式传到前端。

最好选择传到文件服务器或者其他更好的方式。两张图片的base64很大,传输比较耗时。其次,如果请求验证码的接口被攻击,base64传输图片的方式会照成服务器瘫痪。

    @PostMapping("/getImg")
    @ResponseBody
    public JsonResponse<Map<String, Object>> createCodePlus() throws Exception {
        JsonResponse<Map<String, Object>> responseTemp = new JsonResponse<Map<String, Object>>();
        Map<String, Object> rtnMap = new HashMap<String, Object>();

        //随机选择目标图片
        Random randomImages = new Random();
        String picPath = "templates/randomImg/" + randomImages.nextInt(20) + ".png";
        picPath = URLDecoder.decode(picPath,"utf-8");
        Resource resource = new ClassPathResource(picPath);
        //获取坐标、大小图片
        rtnMap = RandomCutPicUtils.generateTwoImage(resource.getInputStream());

        rtnMap.remove("xBegin");
        responseTemp.setResult(rtnMap);
        return responseTemp;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值