How to Generate Custom Captcha Images in Spring Boot with Hutool
Learn step‑by‑step how to create various graphical captchas—including line, circle, shear, GIF, and custom numeric or arithmetic types—in a Spring Boot application, using both a hand‑written utility class and the Hutool‑captcha library, with full code examples and controller integration.
Introduction
This article explains how to generate graphical verification codes (captchas) for a login page in a Spring Boot project, covering both a custom utility class and the Hutool‑captcha library.
Hand‑written Captcha
Creating the utility class
public class Code {
public static final String RANDOMCODEKEY = "ValidateCode";
private Random random = new Random();
private String randomString = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
private int width = 80;
private int height = 26;
private int lineSize = 40;
private int stringNum = 4;
private Font getFont() {
return new Font("Fixedsys", Font.CENTER_BASELINE, 18);
}
private Color getRandColor(int fc, int bc) {
if (fc > 255) fc = 255;
if (bc > 255) bc = 255;
int r = fc + random.nextInt(bc - fc - 16);
int g = fc + random.nextInt(bc - fc - 14);
int b = fc + random.nextInt(bc - fc - 18);
return new Color(r, g, b);
}
private String getRandomString(int num) {
return String.valueOf(randomString.charAt(num));
}
private String drawString(Graphics g, String randomString, int i) {
g.setFont(getFont());
g.setColor(new Color(random.nextInt(101), random.nextInt(111), random.nextInt(121)));
String rand = String.valueOf(getRandomString(random.nextInt(this.randomString.length())));
randomString += rand;
g.translate(random.nextInt(3), random.nextInt(3));
g.drawString(rand, 13 * i, 16);
return randomString;
}
private void drawLine(Graphics g) {
int x = random.nextInt(width);
int y = random.nextInt(height);
int xl = random.nextInt(13);
int yl = random.nextInt(15);
g.drawLine(x, y, x + xl, y + yl);
}
public void getValidateCode(HttpServletRequest request, HttpServletResponse response) {
HttpSession session = request.getSession();
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_BGR);
Graphics g = image.getGraphics();
g.fillRect(0, 0, width, height);
g.setFont(new Font("Times New Roman", Font.ROMAN_BASELINE, 18));
g.setColor(getRandColor(110, 133));
for (int i = 0; i <= lineSize; i++) {
drawLine(g);
}
String randomString = "";
for (int i = 1; i <= stringNum; i++) {
randomString = drawString(g, randomString, i);
}
session.removeAttribute(RANDOMCODEKEY);
session.setAttribute(RANDOMCODEKEY, randomString);
System.out.println(randomString);
g.dispose();
try {
ImageIO.write(image, "JPEG", response.getOutputStream());
} catch (Exception e) {
e.printStackTrace();
}
}
}Place this class in a util package; it generates a random image and stores the code in the HTTP session.
Calling from a controller
@GetMapping("/checkCode2")
public void checkcode2(HttpServletRequest request, HttpServletResponse response) {
response.setContentType("image/jpeg");
response.setDateHeader("Expires", 0);
response.setHeader("Pragma", "no-cache");
response.setHeader("Cache-Control", "no-cache");
Code code = new Code();
code.getValidateCode(request, response);
}Verification with Apifox
The endpoint created above can be tested with API testing tools such as Apifox to ensure the image is returned correctly.
Using Hutool Captcha
Adding the dependency
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-captcha</artifactId>
<version>5.8.6</version>
</dependency>Or import the full library with hutool-all if more utilities are needed.
LineCaptcha – line interference
@GetMapping("/checkCode1")
public void checkCode(HttpServletResponse response) throws IOException {
LineCaptcha captcha = CaptchaUtil.createLineCaptcha(130, 38, 5, 5);
response.setHeader("Pragma", "no-cache");
response.setHeader("Cache-Control", "no-cache");
response.setDateHeader("Expires", 0);
response.setContentType("image/jpeg");
captcha.write(response.getOutputStream());
response.getOutputStream().close();
}CircleCaptcha – circular interference
@GetMapping("/checkCode1")
public void checkCode(HttpServletResponse response) throws IOException {
CircleCaptcha captcha = CaptchaUtil.createCircleCaptcha(130, 38, 5, 20);
response.setHeader("Pragma", "no-cache");
response.setHeader("Cache-Control", "no-cache");
response.setDateHeader("Expires", 0);
response.setContentType("image/jpeg");
captcha.write(response.getOutputStream());
response.getOutputStream().close();
}ShearCaptcha – distorted interference
@GetMapping("/checkCode1")
public void checkCode(HttpServletResponse response) throws IOException {
ShearCaptcha captcha = CaptchaUtil.createShearCaptcha(130, 38, 5, 5);
response.setHeader("Pragma", "no-cache");
response.setHeader("Cache-Control", "no-cache");
response.setDateHeader("Expires", 0);
response.setContentType("image/jpeg");
captcha.write(response.getOutputStream());
response.getOutputStream().close();
}GifCaptcha – animated GIF
@GetMapping("/checkCode1")
public void checkCode(HttpServletResponse response) throws IOException {
GifCaptcha captcha = CaptchaUtil.createGifCaptcha(130, 38, 5);
response.setHeader("Pragma", "no-cache");
response.setHeader("Cache-Control", "no-cache");
response.setDateHeader("Expires", 0);
response.setContentType("image/jpeg");
captcha.write(response.getOutputStream());
response.getOutputStream().close();
}Custom Captcha
Numeric captcha
@GetMapping("/checkCode1")
public void checkCode(HttpServletResponse response) throws IOException {
RandomGenerator randomGenerator = new RandomGenerator("0123456789", 4);
LineCaptcha captcha = CaptchaUtil.createLineCaptcha(200, 100);
captcha.setGenerator(randomGenerator);
captcha.createCode();
response.setHeader("Pragma", "no-cache");
response.setHeader("Cache-Control", "no-cache");
response.setDateHeader("Expires", 0);
response.setContentType("image/jpeg");
captcha.write(response.getOutputStream());
response.getOutputStream().close();
}Alphabetic captcha
@GetMapping("/checkCode1")
public void checkCode(HttpServletResponse response) throws IOException {
RandomGenerator randomGenerator = new RandomGenerator("abcdefghijklmnopqrstuvwyzx", 4);
CircleCaptcha captcha = CaptchaUtil.createCircleCaptcha(200, 100);
captcha.setGenerator(randomGenerator);
captcha.createCode();
response.setHeader("Pragma", "no-cache");
response.setHeader("Cache-Control", "no-cache");
response.setDateHeader("Expires", 0);
response.setContentType("image/jpeg");
captcha.write(response.getOutputStream());
response.getOutputStream().close();
}Arithmetic captcha
@GetMapping("/checkCode1")
public void checkCode(HttpServletResponse response) throws IOException {
ShearCaptcha captcha = CaptchaUtil.createShearCaptcha(200, 45, 4, 4);
captcha.setGenerator(new MathGenerator());
captcha.createCode();
response.setHeader("Pragma", "no-cache");
response.setHeader("Cache-Control", "no-cache");
response.setDateHeader("Expires", 0);
response.setContentType("image/jpeg");
captcha.write(response.getOutputStream());
response.getOutputStream().close();
}Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Architect's Guide
Dedicated to sharing programmer-architect skills—Java backend, system, microservice, and distributed architectures—to help you become a senior architect.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
