手把手教你写滑动图片验证码!

本次文章比较无聊,使用场景比较少,我也不知道为什么公司要整这个玩意,不花钱直接买,但是我只是个工具人,上头怎么说我怎么做


手把手教你写滑动图片验证码!
img

需求背景

  • 公司原有的图片验证码是php,现在要抛弃改成java,在此基础上增加滑动验证码。
  • 目前网络讲的清楚的关于滑动验证码实现的很少,所以想着实现了之后share一下。

先放出个效果图先

手把手教你写滑动图片验证码!


需求分析

整个滑动验证码其实划分成三个部分

  • 滑动验证码的底部大图(底图)

  • 类似拼图一样可以拖动的图(滑块图)

  • 和滑块图形状一样的需要被填充的阴影部分(阴影图)

看我干嘛?需求都有了,开整呀!


具体实现

1.导入滑动验证码所需的底部大图

就是一些比较基本的IO和图片操作,自行食用把

/**
 * 获取图片BufferImage
 * <p>
 * 1.读取一张照片
 */
public BufferedImage getPicture() throws IOException {
    String imageName = "image" + RandomUtils.nextInt(5) + ".png";
    String imageString = (String) redisOperations.get(SLIDE_IMAGE_CODE_KEY_PREFIX + imageName);
    if(StringUtils.isEmpty(imageString)){
        InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream("static/img/" + imageName);
        BufferedImage read = ImageIO.read(resourceAsStream);
        String imageStringToRedis = encodeToString(read);
        redisOperations.set(SLIDE_IMAGE_CODE_KEY_PREFIX+imageName,imageStringToRedis);
        return read;
    }
    return decodeToImage(imageString);
}

2.滑块图的实现

仔细观察滑块图,无非就是由一个正方形和两个半圆组合在一块。

球球了!这个两个半圆最好别在同一边。因为是真的丑!!


具体实现步骤及代码如下:

(1)生成滑块图的拼图形状

/**
 * 中心矩形面积添加或减去半圆
 *
 * @param area
 * @param edge
 * @param circularType
 * @return
 */
public Shape createCircular(Area area, int edge, int circularType) {
    Shape circle = null;
    /**
     * cursor(游标):圆的右上角坐标随机化  cursor ∈[Padding+Margin,Padding+Width-Diameter-Margin]
     */
    int cursor = RandomUtils.nextInt(74 - 32 + 1) + 32;
    int margin = JIGSAW_MARGIN - (CIRCLE_DIAMETER / 2);
    if (edge == LEFT_EDGE) {
        circle = new Arc2D.Double(margin, cursor, CIRCLE_DIAMETER, CIRCLE_DIAMETER, 0, 360, Arc2D.OPEN);
    } else if (edge == TOP_EDGE) {
        circle = new Arc2D.Double(cursor, margin, CIRCLE_DIAMETER, CIRCLE_DIAMETER, 0, 360, Arc2D.OPEN);
    } else if (edge == RIGHT_EDGE) {
        circle = new Arc2D.Double(JIGSAW_WIDTH + margin, cursor, CIRCLE_DIAMETER, CIRCLE_DIAMETER, 0, 360, Arc2D.OPEN);
    } else {
        circle = new Arc2D.Double(cursor, JIGSAW_WIDTH + margin, CIRCLE_DIAMETER, CIRCLE_DIAMETER, 0, 360, Arc2D.OPEN);
    }
    /**
     * 判断圆的类型,区域做加减
     */
    if (circularType == CONVEX_CIRCLE) {
        area.add(new Area(circle));
    } else {
        area.subtract(new Area(circle));
    }
    return area;
}

   /**
     * 获取拼图形状
     * 正方形的四边:{ 左边:0,上边:1,右边:2,下边:3 }
     * 凸圆:0 凹圆:1
     *
     * @return
     */
    public Shape getPicShape() {
        int edge = RandomUtils.nextInt(4);
        int circularTypeOne = RandomUtils.nextInt(2);
        int edgeTwo = RandomUtils.nextInt(4);
        while (edgeTwo == edge) {
            edgeTwo = RandomUtils.nextInt(4);
        }
        int circularTypeTwo = RandomUtils.nextInt(2);
        Area area = new Area();
        Shape square = new Rectangle2D.Double(JIGSAW_MARGIN, JIGSAW_MARGIN, JIGSAW_WIDTH, JIGSAW_HEIGHT);
        area.add(new Area(square));
        createCircular(area, edge, circularTypeOne);
        createCircular(area, edgeTwo, circularTypeTwo);
        return area;
    }

主要的逻辑通过一个正方形来两个圆形做并集和差集,示意图如下

手把手教你写滑动图片验证码!

(2)生成滑块图

获取了拼图的形状之后,就要那这个形状随机的在底层上扣出来一块来,但是这个区域一般选择底图的右半部分,而且要注意上边界和下边界的选取一定要在图片内

/**
 * 处理模板图生成不规则图片
 */
public BufferedImage handlerPic(BufferedImage template, Shape imageShape) {

    BufferedImage cut = template;
    Shape shape = imageShape;
    Graphics2D graphics = cut.createGraphics();
    graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
    graphics.draw(shape);
    BufferedImage bfm = new BufferedImage(TAILORING_WIDTH, TAILORING_HEIGHT, BufferedImage.TYPE_INT_ARGB);
    shadowLayer.set(new BufferedImage(TAILORING_WIDTH, TAILORING_HEIGHT, BufferedImage.TYPE_INT_ARGB));
    for (int i = 0; i < TAILORING_WIDTH; i++) {
        for (int j = 0; j < TAILORING_HEIGHT; j++) {
            if (shape.contains(i, j)) {
                bfm.setRGB(i, j, template.getRGB(i, j));
            }
            if (shape.contains(i, j)) {
                shadowLayer.get().setRGB(i, j, Color.black.getRGB());
            }
        }
    }
    BufferedImage resultImgBuff = dealLightAndShadow(bfm, shape);
    return resultImgBuff;

}

3.阴影图的实现

在上面的过程中,我们已经生成了滑块图的形如拼图的形状,那我们就可以用这个形状去底图生成阴影图,就是将底图处理成带有阴影图的样子。

/**
 * 在大图增加阴影部分,即填充部分
 *
 * @param image
 * @param sourceImage
 * @return
 */
public BufferedImage addWaterMark(BufferedImage image, BufferedImage sourceImage, Shape shape) {
    BufferedImage source = sourceImage;
    Graphics2D graphics = source.createGraphics();
    graphics.setColor(Color.white);
    graphics.setStroke(new BasicStroke(SHADOW_WIDTH));
    graphics.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, 0.6F));
    BufferedImage waterMark = image;
    handler(waterMark.createGraphics(), SHADOW_WIDTH, shape);
    shadowHandle(waterMark.createGraphics(), SHADOW_WIDTH, shape);
    shadowHandleBlack(waterMark.createGraphics(), SHADOW_WIDTH, shape);
    graphics.drawImage(waterMark, locationX.get(), locationY.get(), null);
    graphics.dispose();
    return source;
}

    /**
     * 水印内发光(增加阴影)
     *
     * @param g2
     * @param shadowWidth
     * @param clipShape
     */
    public void handler(Graphics2D g2, int shadowWidth, Shape clipShape) {
        int gw = shadowWidth * 8;
        for (int i = gw; i >= 2; i -= 2) {
            float pct = (float) (gw - i) / (gw - 1);
            Color mixHi = getMixedColor(OUTER_SHADOW_COLOR, pct, INNER_SHADOW_COLOR, 1.0f - pct);
            Color mixLo = getMixedColor(OUTER_SHADOW_COLOR, pct, INNER_SHADOW_COLOR, 1.0f - pct);
            g2.setPaint(new GradientPaint(0.0f, 35 * 0.25f, mixHi, 0, 35, mixLo));
            g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, pct));
            g2.setStroke(new BasicStroke(i));
            g2.draw(clipShape);
        }
    }
       /**
     * 处理水印白边情况
     *
     * @param g2
     * @param shadowWidth
     * @param clipShape
     */
    private void shadowHandle(Graphics2D g2, int shadowWidth, Shape clipShape) {
        g2.setColor(Color.black);
        g2.setStroke(new BasicStroke(1));
        g2.draw(clipShape);
    }

    /**
     * 水印边框描白
     *
     * @param g2
     * @param shadowWidth
     * @param clipShape
     */
    private void shadowHandleBlack(Graphics2D g2, int shadowWidth, Shape clipShape) {
        g2.setColor(WATER_MARK_PADDING_COLOR);
        g2.setStroke(new BasicStroke(1));
        g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, 1F));
        g2.draw(clipShape);
    }

上面代码还增加一些处理阴影图效果的方法的代码

萝卜青菜各有所爱,自己想要什么样的自己调去。


总结

全部代码放在这里(需求各位客官动动小手登录之后才能看到)👇

https://gitee.com/TyroneMax/captcha?_from=gitee_search


仓库里面包含了滑动验证码的校验,具体实现还有一个前端demo

写在最后

当然啦,登山的路有很多,这只是其中一条。如果以后我能找到更好的方案,也会再share一下。


我仍然是废物,请多指教!

原文始发于微信公众号(Hephaestuses):手把手教你写滑动图片验证码!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/45047.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!