栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

一种前后端滑动验证码方案

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

一种前后端滑动验证码方案

概述

实际开发中,很常见的一种需求,即拦截机器人登陆,增加略微复杂的滑动验证码(相对于静态验证码而言)。

@RequestMapping("checkcaptcha")
public String checkCaptcha(@RequestBody JSONObject jsonObject) {
    String captchaToken = jsonObject.getString("captchtoken");
    Integer moveLength = jsonObject.getInteger("moveLength");
    if (StringUtils.isEmpty(captchaToken)) {
        return JSONObject.toJSONString("验证不通过");
    }
    try {
        String token = RedisTool.getValueByKey(jedisCluster, captchaToken);
        if (StringUtils.isEmpty(token)) {
            return JSONObject.toJSONString("验证过期,请重试");
        }
        Integer xWidth = Integer.parseInt(RedisTool.getValueByKey(jedisCluster, captchaToken).trim());
        // 精度控制
        if (Math.abs(xWidth - moveLength) > 10) {
            RedisTool.delKey(jedisCluster, captchaToken);
            return JSONObject.toJSONString("验证不通过");
        }
    } catch (Exception e) {
        RedisTool.delKey(jedisCluster, captchaToken);
        return JSONObject.toJSONString(e.getMessage());
    }
    return JSONObject.toJSONString("success");
}

@RequestMapping(value = "/captcha")
public String getCaptchaImage() {
    VerificationCodePlace vcPlace = VerificationCodeAdapter.getRandomVerificationCodePlace(this.getClass().getResource("/static/captcha/after/").getPath(),
            this.getClass().getResource("/static/captcha/image/").getPath());
    try {
        String token = UUID.randomUUID().toString();
        RedisTool.setKeyValueExpire(jedisCluster, token, vcPlace.getXLocation().toString(), 60 * 2);
        vcPlace.setXLocation(0);
        vcPlace.setCaptchtoken(token);
        return JSONObject.toJSONString(vcPlace);
    } catch (Exception e) {
        return JSONObject.toJSONString(e.getMessage());
    }
}

POJO

@Data
public class VerificationCodePlace {
    private String backName;
    private String markName;
    private Integer xLocation;
    private Integer yLocation;
    private String captchtoken;

    public VerificationCodePlace(String backName, String markName, Integer xLocation, Integer yLocation) {
        this.backName = backName;
        this.markName = markName;
        this.xLocation = xLocation;
        this.yLocation = yLocation;
    }

}
@Slf4j
public class VerificationCodeAdapter {

    
    private static final int CUT_WIDTH = 50;
    
    private static final int CUT_HEIGHT = 50;
    
    private static final int circleR = 5;
    
    private static final int RECTANGLE_PADDING = 8;
    
    private static final int SLIDER_IMG_OUT_PADDING = 1;

    // 生成拼图样式
    private static int[][] getBlockData() {
        int[][] data = new int[CUT_WIDTH][CUT_HEIGHT];
        Random random = new Random();
        //(x-a)²+(y-b)²=r²
        //x中心位置左右5像素随机
        double x1 = RECTANGLE_PADDING + (CUT_WIDTH - 2 * RECTANGLE_PADDING) / 2.0 - 5 + random.nextInt(10);
        //y 矩形上边界半径-1像素移动
        double y1_top = RECTANGLE_PADDING - random.nextInt(3);
        double y1_bottom = CUT_HEIGHT - RECTANGLE_PADDING + random.nextInt(3);
        double y1 = random.nextInt(2) == 1 ? y1_top : y1_bottom;


        double x2_right = CUT_WIDTH - RECTANGLE_PADDING - circleR + random.nextInt(2 * circleR - 4);
        double x2_left = RECTANGLE_PADDING + circleR - 2 - random.nextInt(2 * circleR - 4);
        double x2 = random.nextInt(2) == 1 ? x2_right : x2_left;
        double y2 = RECTANGLE_PADDING + (CUT_HEIGHT - 2 * RECTANGLE_PADDING) / 2.0 - 4 + random.nextInt(10);

        double po = Math.pow(circleR, 2);
        for (int i = 0; i < CUT_WIDTH; i++) {
            for (int j = 0; j < CUT_HEIGHT; j++) {
                //矩形区域
                boolean fill;
                if ((i >= RECTANGLE_PADDING && i < CUT_WIDTH - RECTANGLE_PADDING)
                        && (j >= RECTANGLE_PADDING && j < CUT_HEIGHT - RECTANGLE_PADDING)) {
                    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 < CUT_WIDTH; i++) {
            for (int j = 0; j < CUT_HEIGHT; j++) {
                //四个正方形边角处理
                for (int k = 1; k <= SLIDER_IMG_OUT_PADDING; k++) {
                    //左上、右上
                    if (i >= RECTANGLE_PADDING - k && i < RECTANGLE_PADDING
                            && ((j >= RECTANGLE_PADDING - k && j < RECTANGLE_PADDING)
                            || (j >= CUT_HEIGHT - RECTANGLE_PADDING - k && j < CUT_HEIGHT - RECTANGLE_PADDING + 1))) {
                        data[i][j] = 2;
                    }

                    //左下、右下
                    if (i >= CUT_WIDTH - RECTANGLE_PADDING + k - 1 && i < CUT_WIDTH - RECTANGLE_PADDING + 1) {
                        for (int n = 1; n <= SLIDER_IMG_OUT_PADDING; n++) {
                            if (((j >= RECTANGLE_PADDING - n && j < RECTANGLE_PADDING)
                                    || (j >= CUT_HEIGHT - RECTANGLE_PADDING - n && j <= CUT_HEIGHT - RECTANGLE_PADDING))) {
                                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 < CUT_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 < CUT_WIDTH && data[i + SLIDER_IMG_OUT_PADDING][j] == 0) {
                    data[i + SLIDER_IMG_OUT_PADDING][j] = 2;
                }
            }
        }
        return data;
    }

    // 抠出拼图
    private static void cutImgByTemplate(BufferedImage oriImage, BufferedImage targetImage, int[][] blockImage, int x, int y) {
        int[][] martrix = new int[3][3];
        int[] values = new int[9];
        for (int i = 0; i < CUT_WIDTH; i++) {
            for (int j = 0; j < CUT_HEIGHT; j++) {
                int _x = x + i;
                int _y = y + j;
                int rgbFlg = blockImage[i][j];
                int rgb_ori = oriImage.getRGB(_x, _y);
                // 原图中对应位置变色处理
                if (rgbFlg == 1) {
                    //抠图上复制对应颜色值
                    targetImage.setRGB(i, j, rgb_ori);
                    //原图对应位置颜色变化
//                    oriImage.setRGB(_x, _y, Color.LIGHT_GRAY.getRGB());

                    //抠图区域高斯模糊
                    readPixel(oriImage, _x, _y, values);
                    fillMatrix(martrix, values);
                    oriImage.setRGB(_x, _y, avgMatrix(martrix));
                } else if (rgbFlg == 2) {
                    targetImage.setRGB(i, j, Color.WHITE.getRGB());
                    oriImage.setRGB(_x, _y, Color.GRAY.getRGB());
                } else if (rgbFlg == 0) {
                    //int alpha = 0;
//                    targetImage.setRGB(i, j, rgb_ori & 0x00ffffff);
                }
            }

        }
    }

    private static void readPixel(BufferedImage img, int x, int y, int[] pixels) {
        int xStart = x - 1;
        int yStart = y - 1;
        int current = 0;
        for (int i = xStart; i < 3 + xStart; i++) {
            for (int j = yStart; j < 3 + yStart; j++) {
                int tx = i;
                if (tx < 0) {
                    tx = -tx;

                } else if (tx >= img.getWidth()) {
                    tx = x;
                }
                int ty = j;
                if (ty < 0) {
                    ty = -ty;
                } else if (ty >= img.getHeight()) {
                    ty = y;
                }
                pixels[current++] = img.getRGB(tx, ty);
            }
        }
    }

    private static void fillMatrix(int[][] matrix, int[] values) {
        int filled = 0;
        for (int[] x : matrix) {
            for (int j = 0; j < x.length; j++) {
                x[j] = values[filled++];
            }
        }
    }

    private static int avgMatrix(int[][] matrix) {
        int r = 0;
        int g = 0;
        int b = 0;
        for (int[] x : matrix) {
            for (int j = 0; j < x.length; j++) {
                if (j == 1) {
                    continue;
                }
                Color c = new Color(x[j]);
                r += c.getRed();
                g += c.getGreen();
                b += c.getBlue();
            }
        }
        return new Color(r / 8, g / 8, b / 8).getRGB();
    }

    
    private static BufferedImage getBufferedImage(String path) throws IOException {
        File file = new File(path);
        if (file.isFile()) {
            return ImageIO.read(file);
        }
        return null;
    }

    // 处理存放
    private static VerificationCodePlace cutAndSave(String imgName, String path, int[][] data, String headPath) throws Exception {
        VerificationCodePlace vcPlace =
                new VerificationCodePlace("sample_after.png", "sample_after_mark.png", 112, 50);

        // 进行图片处理
        BufferedImage originImage = getBufferedImage(path);
        if (originImage != null) {
            int locationX = 90 + new Random().nextInt(originImage.getWidth() - CUT_WIDTH * 3);
            int locationY = new Random().nextInt(originImage.getHeight() - CUT_HEIGHT) / 2;
            BufferedImage markImage = new BufferedImage(CUT_WIDTH, CUT_HEIGHT, BufferedImage.TYPE_4BYTE_ABGR);
            cutImgByTemplate(originImage, markImage, data, locationX, locationY);
            vcPlace = new VerificationCodePlace(getImagebase64(originImage), getImagebase64(markImage), locationX, locationY);
        }

        return vcPlace;
    }

    
    private static String getImagebase64(BufferedImage image) throws IOException {
        ByteArrayOutputStream bao = new ByteArrayOutputStream();
        ImageIO.write(image, "png", bao);
        byte[] imagedata = bao.toByteArray();
        base64Encoder encoder = new base64Encoder();
        String base64Image = encoder.encodeBuffer(imagedata).trim();
        // 删除 rn
        base64Image = base64Image.replaceAll("r|n", "");
        return base64Image;
    }

    
    private static ArrayList getFileNamesFromDic(String dicPath) {
        File dic = new File(dicPath);
        ArrayList imageFileNames = new ArrayList<>();
        File[] dicFileList = dic.listFiles();
        for (File f : dicFileList) {
            imageFileNames.add(f.getName());
        }
        return imageFileNames;
    }

    
    public static VerificationCodePlace getRandomVerificationCodePlace(String headPath, String imageUrl) {
        VerificationCodePlace vcPlace = new VerificationCodePlace("sample_after.png", "sample_mark_after.png", 112, 50);

        // 从文件夹中读取所有待选择文件
        ArrayList imageFileNames = getFileNamesFromDic(imageUrl);

        // 随机获取
        int r = (int) Math.round(Math.random() * (imageFileNames.size() - 1));
        String imgName = imageFileNames.get(r);
        String path = imageUrl + imgName;
        int[][] data = VerificationCodeAdapter.getBlockData();

        // 进行图片处理
        try {
            vcPlace = cutAndSave(imgName, path, data, headPath);
        } catch (Exception e) {
            log.error("getRandomVerificationCodePlace failed: " + e.getMessage());
            return vcPlace;
        }
        return vcPlace;
    }

}
优化

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/273620.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号