SpringBoot接入图片验证码

344 阅读2分钟

实现验证码图片的生成可以使用第三方库,比如 Google 的 Kaptcha 库。在 Spring Boot 中,可以通过在 pom.xml 文件中添加以下依赖来引入 Kaptcha 库:

<dependency>
    <groupId>com.github.penggle</groupId>
    <artifactId>kaptcha</artifactId>
    <version>2.3.2</version>
</dependency>

然后在 Controller 中添加生成验证码的代码,示例如下:

import java.awt.image.BufferedImage;
import java.util.Properties;

import javax.imageio.ImageIO;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.google.code.kaptcha.impl.DefaultKaptcha;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
@RequestMapping("captcha")
public class CaptchaController {
    @GetMapping("/captcha")
    public void captcha(HttpServletRequest request, HttpServletResponse response) throws Exception {
        // 创建 Kaptcha 对象
        DefaultKaptcha captcha = new DefaultKaptcha();
        // 设置验证码的宽度和高度
        Properties properties = new Properties();
        properties.setProperty("kaptcha.border", "no");
        properties.setProperty("kaptcha.border.color", "34,114,200");
        properties.setProperty("kaptcha.image.width", "200");
        properties.setProperty("kaptcha.image.height", "50");
        //properties.setProperty("kaptcha.textproducer.char.string", "0123456789");
        properties.setProperty("kaptcha.textproducer.char.length", "6");
        properties.setProperty("kaptcha.textproducer.font.names", "Arial,Arial Narrow,Serif,Helvetica,Tahoma,Times New Roman,Verdana");
        properties.setProperty("kaptcha.textproducer.font.size", "38");

        properties.setProperty("kaptcha.background.clear.from", "white");
        properties.setProperty("kaptcha.background.clear.to", "white");

        com.google.code.kaptcha.util.Config config = new com.google.code.kaptcha.util.Config(properties);
        captcha.setConfig(config);
        // 生成验证码字符串
        String text = captcha.createText();
        // 将验证码字符串存储在 session 中
        request.getSession().setAttribute("captcha", text);
        // 将验证码图片输出到浏览器
        response.setContentType("image/png");
        ServletOutputStream outputStream = response.getOutputStream();
        BufferedImage image = captcha.createImage(text);
        ImageIO.write(image, "png", outputStream);
        outputStream.flush();
        outputStream.close();
    }
}

实现一个简单的前端页面,用于显示验证码

<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>验证码</title>
    <link rel="stylesheet" href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/5.2.3/css/bootstrap.min.css">
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.4/jquery.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/5.2.3/js/bootstrap.min.js"></script>
</head>
<body>



<nav class="navbar navbar-default">

</nav>

<div class="container">
    <div class="form-group">
        <label for="captcha">Please enter the code below:</label>
        <div class="input-group">
            <input type="text" id="captcha" name="captcha" class="form-control">
            <span class="input-group-addon">
              <img id="captcha-image" src="captcha/captcha" alt="CAPTCHA code">
            </span>
        </div>
    </div>
    <div class="form-group">
        <button id="refresh-captcha" class="btn btn-default">Refresh CAPTCHA</button>
    </div>
</div>

<footer class="container-fluid text-center">
    <p>Footer Text</p>
</footer>


<script>
    $(document).ready(function() {
      $('#refresh-captcha').click(function() {
        $('#captcha-image').attr('src', 'captcha/captcha?' + new Date().getTime());
      });
      $('#captcha-image').click(function() {
        $('#captcha-image').attr('src', 'captcha/captcha?' + new Date().getTime());
      });
    });

</script>
</body>
</html>

结果大体长这样:

image.png

如果在在linux系统上运行报错: org.springframework.web.util.NestedServletException: Handler dispatch failed; nested exception is java.lang.InternalError: java.lang.reflect.InvocationTargetException

原因:操作系统没有FontConfig组件

解决:安装相应字体配置组件

  • yum install fontconfig
  • fc-cache –force

如果不用Kapcha,可以:

private static final int LINE_WIDTH = 2;

    /**
     * getImageBuffer
     *
     * @param resultCode resultCode
     * @return BufferedImage
     */
    private static BufferedImage getImageBuffer(String resultCode, int w, int h) {
        // 在内存中创建图象
        final BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
        // 获取图形上下文
        final Graphics2D graphics = (Graphics2D) image.getGraphics();
        // 设定背景颜色
        graphics.setColor(Color.WHITE); // ---1
        graphics.fillRect(0, 0, w, h);
        // 设定边框颜色
//		graphics.setColor(getRandColor(100, 200)); // ---2
        graphics.drawRect(0, 0, w - 1, h - 1);

        final Random random = new Random();
        // 随机产生干扰线,使图象中的认证码不易被其它程序探测到
        for (int i = 0; i < 200; i++) {
            graphics.setColor(getRandColor(150, 200)); // ---3

            final int x = random.nextInt(w - 2 - 1) + 1; // 保证画在边框之内
            final int y = random.nextInt(h - 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(new Color(20 + random.nextInt(130), 20 + random
            // .nextInt(130), 20 + random.nextInt(130)));
            // 设置字体颜色
            graphics.setColor(Color.BLACK);
            // 设置字体样式
//			graphics.setFont(new Font("Arial Black", Font.ITALIC, 18));
            graphics.setFont(new Font("Times New Roman", Font.BOLD, 24));
            // 设置字符,字符间距,上边距
            graphics.drawString(String.valueOf(resultCode.charAt(i)), (23 * i) + 8, 26);
        }
        // 图象生效
        graphics.dispose();
        return image;
    }

    /**
     * getRandColor
     *
     * @param fc fc
     * @param bc bc
     * @return Color
     */
    private static Color getRandColor(int fc, int bc) { // 取得给定范围随机颜色
        final Random random = new Random();
        if (fc > 255) {
            fc = 255;
        }
        if (bc > 255) {
            bc = 255;
        }

        final int r = fc + random.nextInt(bc - fc);
        final int g = fc + random.nextInt(bc - fc);
        final int b = fc + random.nextInt(bc - fc);

        return new Color(r, g, b);
    }

结果长这样:

image.png

image.png