diff --git a/pangu-system/src/main/java/com/pangu/web/controller/system/SysLoginController.java b/pangu-system/src/main/java/com/pangu/web/controller/system/SysLoginController.java index 50f1ac3..3893941 100644 --- a/pangu-system/src/main/java/com/pangu/web/controller/system/SysLoginController.java +++ b/pangu-system/src/main/java/com/pangu/web/controller/system/SysLoginController.java @@ -3,8 +3,12 @@ package com.pangu.web.controller.system; import com.pangu.common.core.domain.AjaxResult; import org.springframework.web.bind.annotation.*; -import java.util.HashMap; -import java.util.Map; +import javax.imageio.ImageIO; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.io.ByteArrayOutputStream; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; /** * 登录控制器 @@ -14,6 +18,11 @@ import java.util.Map; @RestController public class SysLoginController { + /** + * 验证码存储(开发阶段使用内存,生产环境应使用Redis) + */ + private static final Map CAPTCHA_CACHE = new ConcurrentHashMap<>(); + /** * 获取用户信息 */ @@ -39,6 +48,27 @@ public class SysLoginController { */ @PostMapping("/api/login") public AjaxResult login(@RequestBody Map loginBody) { + String username = loginBody.get("username"); + String password = loginBody.get("password"); + String code = loginBody.get("code"); + String uuid = loginBody.get("uuid"); + + // 验证码校验 + if (uuid != null && !uuid.isEmpty()) { + String cachedCode = CAPTCHA_CACHE.remove(uuid); + if (cachedCode == null) { + return AjaxResult.error("验证码已过期"); + } + if (code == null || !cachedCode.equalsIgnoreCase(code)) { + return AjaxResult.error("验证码错误"); + } + } + + // 用户名密码校验(开发阶段简单校验) + if (!"admin".equals(username) || !"admin123".equals(password)) { + return AjaxResult.error("用户名或密码错误"); + } + Map data = new HashMap<>(); data.put("token", "mock-token-" + System.currentTimeMillis()); return AjaxResult.success(data); @@ -57,10 +87,98 @@ public class SysLoginController { */ @GetMapping("/api/captchaImage") public AjaxResult getCaptchaImage() { + String uuid = UUID.randomUUID().toString(); + String code = generateCaptchaCode(4); + + // 存储验证码 + CAPTCHA_CACHE.put(uuid, code); + + // 生成验证码图片 + String imgBase64 = generateCaptchaImage(code); + Map data = new HashMap<>(); - data.put("uuid", "mock-uuid-" + System.currentTimeMillis()); - data.put("img", "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg=="); - data.put("captchaEnabled", false); + data.put("uuid", uuid); + data.put("img", "data:image/png;base64," + imgBase64); + data.put("captchaEnabled", true); return AjaxResult.success(data); } + + /** + * 生成随机验证码字符 + */ + private String generateCaptchaCode(int length) { + String chars = "ABCDEFGHJKMNPQRSTUVWXYZabcdefghjkmnpqrstuvwxyz23456789"; + Random random = new Random(); + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < length; i++) { + sb.append(chars.charAt(random.nextInt(chars.length()))); + } + return sb.toString(); + } + + /** + * 生成验证码图片(Base64编码) + */ + private String generateCaptchaImage(String code) { + int width = 120; + int height = 40; + + BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); + Graphics2D g = image.createGraphics(); + + // 设置抗锯齿 + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + + // 背景色 + g.setColor(new Color(240, 240, 240)); + g.fillRect(0, 0, width, height); + + // 绘制干扰线 + Random random = new Random(); + g.setColor(new Color(200, 200, 200)); + for (int i = 0; i < 6; i++) { + int x1 = random.nextInt(width); + int y1 = random.nextInt(height); + int x2 = random.nextInt(width); + int y2 = random.nextInt(height); + g.drawLine(x1, y1, x2, y2); + } + + // 绘制验证码文字 + g.setFont(new Font("Arial", Font.BOLD, 28)); + Color[] colors = { + new Color(65, 105, 225), + new Color(220, 20, 60), + new Color(34, 139, 34), + new Color(255, 140, 0) + }; + + for (int i = 0; i < code.length(); i++) { + g.setColor(colors[i % colors.length]); + // 随机旋转角度 + double angle = (random.nextDouble() - 0.5) * 0.3; + g.rotate(angle, 25 + i * 25, 28); + g.drawString(String.valueOf(code.charAt(i)), 15 + i * 25, 30); + g.rotate(-angle, 25 + i * 25, 28); + } + + // 绘制噪点 + for (int i = 0; i < 50; i++) { + int x = random.nextInt(width); + int y = random.nextInt(height); + g.setColor(new Color(random.nextInt(200), random.nextInt(200), random.nextInt(200))); + g.fillRect(x, y, 1, 1); + } + + g.dispose(); + + // 转换为Base64 + try { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ImageIO.write(image, "png", baos); + return Base64.getEncoder().encodeToString(baos.toByteArray()); + } catch (Exception e) { + return ""; + } + } } diff --git a/pangu-ui/src/views/login/index.vue b/pangu-ui/src/views/login/index.vue index 04145c3..6f3896e 100644 --- a/pangu-ui/src/views/login/index.vue +++ b/pangu-ui/src/views/login/index.vue @@ -143,9 +143,14 @@ const handleLogin = () => { }) if (res.code === 200) { - userStore.setToken(res.token) + // token在res.data.token或res.token中 + const token = res.data?.token || res.token + userStore.setToken(token) ElMessage.success('登录成功') router.push('/') + } else { + ElMessage.error(res.msg || '登录失败') + getCaptcha() } } catch (error) { // 登录失败刷新验证码