实现验证码图片的生成可以使用第三方库,比如 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>
结果大体长这样:
如果在在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);
}
结果长这样: