fix: 启用验证码并生成图片

- captchaEnabled 改为 true
- 使用 Java AWT 生成验证码图片(4位随机字符)
- 登录时校验验证码并从内存 Map 移除
- 验证码图片为 PNG 格式 Base64 编码
This commit is contained in:
神码-方晓辉 2026-02-02 09:13:59 +08:00
parent 91676531b8
commit aaf3b0e88f
1 changed files with 90 additions and 4 deletions

View File

@ -3,6 +3,10 @@ package com.pangu.web.controller;
import com.pangu.common.core.domain.AjaxResult; import com.pangu.common.core.domain.AjaxResult;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.util.*; import java.util.*;
/** /**
@ -16,13 +20,79 @@ public class LoginController {
* 获取验证码 * 获取验证码
*/ */
@GetMapping("/captchaImage") @GetMapping("/captchaImage")
public AjaxResult getCaptchaImage() { public AjaxResult getCaptchaImage() throws Exception {
AjaxResult result = AjaxResult.success(); AjaxResult result = AjaxResult.success();
// 开发阶段禁用验证码
result.put("captchaEnabled", false); String uuid = UUID.randomUUID().toString();
result.put("uuid", UUID.randomUUID().toString()); String code = generateRandomCode(4);
// 生成验证码图片
String base64Img = generateCaptchaImage(code);
result.put("captchaEnabled", true);
result.put("img", base64Img);
result.put("uuid", uuid);
// 存储验证码到内存实际应存 Redis这里简化为静态 Map
captchaStore.put(uuid, code.toLowerCase());
return result; return result;
} }
// 简易验证码存储生产环境应使用 Redis
private static final Map<String, String> captchaStore = new HashMap<>();
/**
* 生成随机验证码
*/
private String generateRandomCode(int length) {
String chars = "ABCDEFGHJKLMNPQRSTUVWXYZabcdefghjkmnpqrstuvwxyz23456789";
Random random = new Random();
StringBuilder code = new StringBuilder();
for (int i = 0; i < length; i++) {
code.append(chars.charAt(random.nextInt(chars.length())));
}
return code.toString();
}
/**
* 生成验证码图片
*/
private String generateCaptchaImage(String code) throws Exception {
int width = 100;
int height = 40;
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, 25));
Random random = new Random();
Color[] colors = {Color.RED, Color.BLUE, Color.GREEN, Color.ORANGE, Color.MAGENTA};
for (int i = 0; i < code.length(); i++) {
g.setColor(colors[random.nextInt(colors.length)]);
g.drawString(String.valueOf(code.charAt(i)), 15 + i * 20, 28);
}
// 干扰线
for (int i = 0; i < 3; i++) {
g.setColor(colors[random.nextInt(colors.length)]);
g.drawLine(random.nextInt(width), random.nextInt(height),
random.nextInt(width), random.nextInt(height));
}
g.dispose();
// Base64
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write(image, "png", baos);
return Base64.getEncoder().encodeToString(baos.toByteArray());
}
/** /**
* 登录 * 登录
@ -31,6 +101,22 @@ public class LoginController {
public AjaxResult login(@RequestBody Map<String, String> loginBody) { public AjaxResult login(@RequestBody Map<String, String> loginBody) {
String username = loginBody.get("username"); String username = loginBody.get("username");
String password = loginBody.get("password"); String password = loginBody.get("password");
String code = loginBody.get("code");
String uuid = loginBody.get("uuid");
// 验证码校验
if (code == null || uuid == null) {
return AjaxResult.error("验证码不能为空");
}
String storedCode = captchaStore.get(uuid);
if (storedCode == null) {
return AjaxResult.error("验证码已过期");
}
if (!storedCode.equals(code.toLowerCase())) {
return AjaxResult.error("验证码错误");
}
// 验证后移除
captchaStore.remove(uuid);
// 开发阶段简单校验admin/admin123 // 开发阶段简单校验admin/admin123
if ("admin".equals(username) && "admin123".equals(password)) { if ("admin".equals(username) && "admin123".equals(password)) {