Java-Web生成图片验证码(一)

举报
六月暴雪飞梨花 发表于 2022/09/18 20:31:02 2022/09/18
【摘要】 验证码(CAPTCHA)是“Completely Automated Public Turing test to tell Computers and Humans Apart”(全自动区分计算机和人类的图灵测试)的缩写,是一种区分用户是计算机还是人的公共全自动程序。可以防止:恶意破解密码、刷票、论坛灌水,有效防止某个黑客对某一个特定注册用户用特定程序暴力破解方式进行不断的登陆尝试,实际上用验证码


1 验证码


1.1 什么是验证码?

我搜索百度,从百度百科中拿出来一段话:

验证码(CAPTCHA)是“Completely Automated Public Turing test to tell Computers and Humans Apart”(全自动区分计算机和人类的图灵测试)的缩写,是一种区分用户是计算机还是人的公共全自动程序。可以防止:恶意破解密码、刷票、论坛灌水,有效防止某个黑客对某一个特定注册用户用特定程序暴力破解方式进行不断的登陆尝试,实际上用验证码是现在很多网站通行的方式,我们利用比较简易的方式实现了这个功能。这个问题可以由计算机生成并评判,但是必须只有人类才能解答。由于计算机无法解答CAPTCHA的问题,所以回答出问题的用户就可以被认为是人类。


1.2 验证码的历史

验证码这个词最早是在2002年出现在卡内基梅隆大学。


1.3 验证码的作用

防止恶意破解密码、刷票、论坛灌水、刷页


1.4 验证码的分类

验证码整体来说分为五类,他们分别是

  • 静态图片内容验证码:一般都是输入一些随机码,形式上还可以有计算
  • Gif动画验证码:动态展示一些随机码,形式上还可以有计算
  • 手机短信验证码:发送短信随机验证码
  • 手机语音验证码:语音验证码,也可以让你读出一些随机码或计算结果
  • 视频验证码:点击视频中的一些随机码,计算结果

了解了验证码之后,我们开始手工实现一些验证码,当然你也可以使用一些已经封装好的工具来实现这个功能,例如Hutool、Google、Baidu、阿里等大佬们封装好的一些工具类直接实现验证码。


2 实现一个验证码

如果我们想要实现一个验证码,那么就得先要了解下验证码的实现原理以及代码实现。


2.1 验证码的原理

网页之间实现验证码大体上有如下步骤:

1)生成一个随机数

2)将随机数写入图片

3)将图片返回到网页

4)用户获取到图片信息,输入图片内容

5)用户提交内容,服务端验证内容的准确性


整体的流程,使用一张图可能会更形象些,大家可以在这图里面体会下。

image.png

这里简单使用“静态图片内容验证码”来作为一个实验例子,来实现我们需要的验证码功能。


2.1 生成一个随机码

生成一个随机码的步骤大体如下:

1)创建一个类RandImagesVerifCode

2)编写一个名叫randomString(String baseString, int length)的方法

3)测试实验


完整的代码如下:

package com.liuyc.tooljdk.image;

import java.util.concurrent.ThreadLocalRandom;

/**
 * <p> Picture Verification Code </p>
 *
 * @author Aion.Liu
 * @version v1.0.0
 * @description TODO
 * @since 2022/9/1 22:24
 */
public class RandImagesVerifCode {

    public static final String BASE_NUMBER = "0123456789";

    public static void main(String[] args) {
        String resultCode = RandImagesVerifCode.randomString(BASE_NUMBER, 4);
        System.out.println(resultCode);

    }

    /**
     * 生成一个随机数
     * @param baseString 指定的源中获取随机数
     * @param length 获取随机数的个数
     * @return
     */
    public static String randomString(String baseString, int length) {
        if (baseString == null || baseString == "") {
            return "";
        } else {
            StringBuilder sb = new StringBuilder(length);
            if (length < 1) {
                length = 1;
            }

            int baseLength = baseString.length();

            for (int i = 0; i < length; ++i) {
                int number = ThreadLocalRandom.current().nextInt(baseLength);
                sb.append(baseString.charAt(number));
            }

            return sb.toString();
        }
    }
    
    // 将验证码写入到redis
    public boolean set(String key, Object value, long time) {
        try {
            if (time > 0L) {
                // this.redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
            } else {
                // this.set(key, value);
            }

            return true;
        } catch (Exception var6) {
            var6.printStackTrace();
            return false;
        }
    }

}

分步骤解析:

1、public static final String BASE_NUMBER = "0123456789";

这里是指定一个数据源头,也就是从这里面随机多少个数字出来。关于数据源头,这里可以任意指定。例如我们也可以指定字母数字组合

public static final String BASE_CHAR_NUMBER = "abcdefghijklmnopqrstuvwxyz0123456789";


2、public static String randomString(String baseString, int length) 

这里是生成随机数的方法,有两个参数。参数的用途已经写在了代码中。长度我们一般使用4~5位,其他少了也不好,多了又太过于麻烦。


3、public boolean set(String key, Object value, long time)

将验证码写入到redis,这是redis的key以及值,当然,还需要设置这个验证码的时效,过期之后这个验证码就不可以再使用,需要我们重新获取。


2.2 将验证码写入一张图片

这个步骤比较简单

1)生成一张图片,将验证码写入图片

2)将图片写入到流中并返回

   
    public static void main(String[] args) throws IOException {
        String resultCode = RandImagesVerifCode.randomString(BASE_NUMBER, 4);
        System.out.println(resultCode);

        String base64 = RandImagesVerifCode.produceImage(resultCode);
        System.out.println(base64);

    }



    public static String produceImage(String resultCode) throws IOException {

        /**
         * 定义图形大小
         */
        final int WIDTH = 105;
        /**
         * 定义图形大小
         */
        final int HEIGHT = 35;

        /**
         * 定义干扰线数量
         */
        final int COUNT = 200;

        /**
         * 干扰线的长度=1.414*lineWidth
         */
        final int LINE_WIDTH = 2;

        /**
         * 生成一张图片,将结果写入到图片
         */
        // 在内存中创建图象
        final BufferedImage image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);

        // 获取图形上下文
        final Graphics2D graphics = (Graphics2D) image.getGraphics();

        // 设定背景颜色
        graphics.setColor(Color.WHITE);
        graphics.fillRect(0, 0, WIDTH, HEIGHT);

        // 设定边框颜色
        graphics.drawRect(0, 0, WIDTH - 1, HEIGHT - 1);

        final Random random = new Random();

        // 随机产生干扰线,使图象中的认证码不易被其它程序探测到
        for (int i = 0; i < COUNT; i++) {

            final Random randomColor = new Random();
            final int r = 150 + randomColor.nextInt(50);
            final int g = 150 + randomColor.nextInt(50);
            final int b = 150 + randomColor.nextInt(50);
            graphics.setColor(new Color(r, g, b));

            // 保证画在边框之内
            final int x = random.nextInt(WIDTH - LINE_WIDTH - 1) + 1;
            final int y = random.nextInt(HEIGHT - LINE_WIDTH - 1) + 1;
            final int xl = random.nextInt(LINE_WIDTH);
            final int yl = random.nextInt(LINE_WIDTH);
            graphics.drawLine(x, y, x + xl, y + yl);
        }
        // 取随机产生的认证码
        for (int i = 0; i < resultCode.length(); i++) {
            // 设置字体颜色
            graphics.setColor(Color.BLACK);
            // 设置字体样式
            graphics.setFont(new Font("Times New Roman", Font.BOLD, 24));
            // 设置字符,字符间距,上边距
            graphics.drawString(String.valueOf(resultCode.charAt(i)), (23 * i) + 8, 26);
        }
        // 图象生效
        graphics.dispose();

        /**
         * 将图片写图图片流中
         */
        ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
        //写入流中
        ImageIO.write(image, "JPEG", byteStream);
        //转换成字节
        byte[] bytes = byteStream.toByteArray();
        //转换成base64串
        String base64 = Base64.getEncoder().encodeToString(bytes).trim();
        //删除 \r\n
        base64 = base64.replaceAll("\n", "").replaceAll("\r", "");

        return  "data:image/jpg;base64," + base64;
    }


执行后的结果如下:

4408


data:image/jpg;base64,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


我们可以使用任意编写一个Html,然后将返回的数据写入img标签的src中,

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<img src="data:image/jpg;base64,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
" />

</body>
</html>


打开网页后调试下结果如下结果。

image.png

当然了,你也可以直接拷贝打印出来的代码在浏览器的搜索栏打开。

现在,我们便完成了网页验证码的开发以及回显示到网页中啦。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。