当网站需要进行用户登录、注册等操作时,为了防止机器人恶意攻击,通常会采用图片验证码的方式来验证用户是否为人类。下面介绍一种基于 Java 实现图片验证码的具体代码,包含验证码生成和校验流程。
当网站需要进行用户登录、注册等操作时,为了防止机器人恶意攻击,通常会采用图片验证码的方式来验证用户是否为人类。下面介绍一种基于 Java 实现图片验证码的具体代码,包含验证码生成和校验流程。
生成验证码图片
验证码生成包含以下几个步骤:
- 生成随机字符串
/**
* 生成指定长度的随机字符串
* @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";
- 创建图片
BufferedImage image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
Graphics2D g = image.createGraphics();
g.setColor(Color.WHITE);
g.fillRect(0, 0, WIDTH, HEIGHT);
- 绘制字符串
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);
}
- 绘制干扰线和干扰点
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);
}
- 将图片转为 byte 数组
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ImageIO.write(image, "JPEG", bos);
byte[] imageBytes = bos.toByteArray();
- 将随机字符串和图片 byte 数组存入 Redis(可选)
存入 Redis 的目的是方便用户后续的验证码校验,可以将随机字符串作为 key,图片 byte 数组作为 value,设置过期时间,保证验证码的时效性。存入 Redis 示例代码:
String key = "captcha:" + randomString;
redisTemplate.opsForValue().set(key, imageBytes, CAPTCHA_EXPIRE_MILLISECONDS, TimeUnit.MILLISECONDS);
校验验证码
验证码校验包含以下几个步骤:
- 获取随机字符串
随机字符串可以从用户提交的表单中获取。
- 获取 Redis 中的图片 byte 数组
从 Redis 中获取随机字符串对应的图片 byte 数组,如果获取失败,说明验证码已失效,需要重新生成验证码。
String key = "captcha:" + randomString;
byte[] imageBytes = redisTemplate.opsForValue().get(key);
if (imageBytes == null || imageBytes.length == 0) {
// 验证码已失效,需要重新生成验证码
return false;
}
- 校验用户提交的验证码和图片中的验证码是否匹配
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实现图片验证码具体代码
基础教程推荐
- 关于List、Map、Stream初始化方式 2023-04-23
- 利用JavaMail发送HTML模板邮件 2023-04-12
- Java聊天室之使用Socket实现传递图片 2023-06-23
- MyBatis-Plus多表联查(动态查询)的项目实践 2023-04-07
- 逐一侦破 网上银行安全漏洞 2024-01-13
- JSP开发之Struts2实现下载功能的实例 2023-08-01
- java – AWS EC2 Micro Instance上的Redis性能 2023-11-09
- java – 用于导入mysql转储文件的Shell脚本 2023-11-07
- Swagger及knife4j的基本使用详解 2023-04-23
- Java ScheduledExecutorService的具体使用 2023-07-15