Java实现图片验证码具体代码

当网站需要进行用户登录、注册等操作时,为了防止机器人恶意攻击,通常会采用图片验证码的方式来验证用户是否为人类。下面介绍一种基于 Java 实现图片验证码的具体代码,包含验证码生成和校验流程。

当网站需要进行用户登录、注册等操作时,为了防止机器人恶意攻击,通常会采用图片验证码的方式来验证用户是否为人类。下面介绍一种基于 Java 实现图片验证码的具体代码,包含验证码生成和校验流程。

生成验证码图片

验证码生成包含以下几个步骤:

  1. 生成随机字符串
/**
 * 生成指定长度的随机字符串
 * @param length 随机字符串长度
 * @return 随机字符串
 */
private static String generateRandomString(int length) {
    StringBuilder sb = new StringBuilder(length);
    for (int i = 0; i < length; i++) {
        int rand = (int) (Math.random() * CHARACTERS.length());
        sb.append(CHARACTERS.charAt(rand));
    }
    return sb.toString();
}

// 可用字符集
private static final String CHARACTERS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
  1. 创建图片
BufferedImage image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
Graphics2D g = image.createGraphics();
g.setColor(Color.WHITE);
g.fillRect(0, 0, WIDTH, HEIGHT);
  1. 绘制字符串
g.setFont(new Font("Arial", Font.BOLD, FONT_SIZE));
for (int i = 0; i < randomString.length(); i++) {
    g.setColor(new Color((int) (Math.random() * 255), (int) (Math.random() * 255), (int) (Math.random() * 255)));
    g.drawString(String.valueOf(randomString.charAt(i)), i * FONT_SIZE + PADDING_LEFT, FONT_BASELINE_Y);
}
  1. 绘制干扰线和干扰点
for (int i = 0; i < LINE_NUM; i++) {
    g.setColor(new Color((int) (Math.random() * 255), (int) (Math.random() * 255), (int) (Math.random() * 255)));
    int x1 = (int) (Math.random() * WIDTH);
    int y1 = (int) (Math.random() * HEIGHT);
    int x2 = (int) (Math.random() * WIDTH);
    int y2 = (int) (Math.random() * HEIGHT);
    g.drawLine(x1, y1, x2, y2);
}

for (int i = 0; i < DOT_NUM; i++) {
    g.setColor(new Color((int) (Math.random() * 255), (int) (Math.random() * 255), (int) (Math.random() * 255)));
    int x = (int) (Math.random() * WIDTH);
    int y = (int) (Math.random() * HEIGHT);
    g.fillOval(x, y, 1, 1);
}
  1. 将图片转为 byte 数组
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ImageIO.write(image, "JPEG", bos);
byte[] imageBytes = bos.toByteArray();
  1. 将随机字符串和图片 byte 数组存入 Redis(可选)

存入 Redis 的目的是方便用户后续的验证码校验,可以将随机字符串作为 key,图片 byte 数组作为 value,设置过期时间,保证验证码的时效性。存入 Redis 示例代码:

String key = "captcha:" + randomString;
redisTemplate.opsForValue().set(key, imageBytes, CAPTCHA_EXPIRE_MILLISECONDS, TimeUnit.MILLISECONDS);

校验验证码

验证码校验包含以下几个步骤:

  1. 获取随机字符串

随机字符串可以从用户提交的表单中获取。

  1. 获取 Redis 中的图片 byte 数组

从 Redis 中获取随机字符串对应的图片 byte 数组,如果获取失败,说明验证码已失效,需要重新生成验证码。

String key = "captcha:" + randomString;
byte[] imageBytes = redisTemplate.opsForValue().get(key);
if (imageBytes == null || imageBytes.length == 0) {
    // 验证码已失效,需要重新生成验证码
    return false;
}
  1. 校验用户提交的验证码和图片中的验证码是否匹配
String userCaptcha = ...; // 用户提交的验证码
String realCaptcha = RecognizeImage.recognize(imageBytes); // 调用图片识别工具获取图片中的验证码
return userCaptcha.equalsIgnoreCase(realCaptcha);

其中,RecognizeImage 是一个图片识别工具类,可以使用 tesseract-ocr、java-ocr 等开源工具实现验证码识别,这里不再赘述。

示例1:Spring Boot 集成 Redis 实现验证码生成和校验

@RestController
@RequestMapping("/captcha")
public class CaptchaController {
    @Resource
    private StringRedisTemplate redisTemplate;

    @GetMapping("/image")
    public ResponseEntity<byte[]> getImageCaptcha(HttpSession session) throws IOException {
        String captcha = generateRandomString(CAPTCHA_LENGTH);
        byte[] imageBytes = generateImageCaptcha(captcha);
        String key = "captcha:image:" + captcha;
        redisTemplate.opsForValue().set(key, captcha, CAPTCHA_EXPIRE_SECONDS, TimeUnit.SECONDS);
        return ResponseEntity.ok().contentType(MediaType.IMAGE_JPEG).body(imageBytes);
    }

    @PostMapping("/verify")
    public boolean verifyCaptcha(@RequestParam String captcha, HttpSession session) {
        String key = "captcha:image:" + captcha;
        String realCaptcha = redisTemplate.opsForValue().get(key);
        if (realCaptcha == null) {
            // 验证码已失效
            return false;
        } else {
            // 验证码校验
            redisTemplate.delete(key); // 删除验证码
            return captcha.equalsIgnoreCase(realCaptcha);
        }
    }
}

示例2:使用 Jedis 实现验证码的存取

public class CaptchaUtil {
    private static final String REDIS_HOST = "localhost";
    private static final int REDIS_PORT = 6379;
    private static final JedisPool jedisPool = new JedisPool(new JedisPoolConfig(), REDIS_HOST, REDIS_PORT);

    // 生成图片验证码并存入 Redis
    public static String generateAndSaveImageCaptcha(int length, int width, int height) throws IOException {
        String captcha = generateRandomString(length);
        byte[] imageBytes = generateImageCaptcha(captcha, width, height);
        try (Jedis jedis = jedisPool.getResource()) {
            jedis.setex(captcha, CAPTCHA_EXPIRE_SECONDS, new String(imageBytes));
        }
        return captcha;
    }

    // 从 Redis 中获取并校验验证码
    public static boolean verifyImageCaptcha(String captcha, String userInput) {
        try (Jedis jedis = jedisPool.getResource()) {
            String realCaptcha = jedis.get(captcha);
            if (realCaptcha == null) {
                // 验证码已失效
                return false;
            } else {
                // 验证码校验
                jedis.del(captcha); // 删除验证码
                String imageString = realCaptcha.substring(1, realCaptcha.length() - 1).replace("\\", "");
                String realUserInput = RecognizeImage.recognize(Base64.getDecoder().decode(imageString));
                return userInput.equalsIgnoreCase(realUserInput);
            }
        }
    }
}

本文标题为:Java实现图片验证码具体代码

基础教程推荐