refactor all

This commit is contained in:
Timi
2026-07-31 01:03:53 +08:00
parent 4e7f340cc6
commit e45809a0db
512 changed files with 14620 additions and 13519 deletions
@@ -3,14 +3,17 @@ package com.imyeyu.api.util;
import com.imyeyu.java.TimiJava;
import com.imyeyu.java.bean.timi.TimiCode;
import com.imyeyu.java.bean.timi.TimiException;
import com.imyeyu.api.bean.CaptchaFrom;
import com.imyeyu.spring.TimiSpring;
import com.imyeyu.spring.util.Redis;
import com.imyeyu.utils.Calc;
import com.imyeyu.utils.Encoder;
import com.imyeyu.utils.Time;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.imageio.ImageIO;
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Font;
@@ -19,6 +22,9 @@ import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
import java.awt.geom.QuadCurve2D;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.UUID;
/**
* 验证码绘制
@@ -28,15 +34,13 @@ import java.awt.image.BufferedImage;
*/
@Slf4j
@Component
@RequiredArgsConstructor
public class CaptchaManager {
/** 会话频率限制时间,毫秒 */
private static final int LOCK_TIME = 1000;
/** 会话频率限制键,插值会话 ID */
private static final String LOCK_KEY = "CAPTCHA:LOCK:%s";
/** 会话值缓存键,插值验证码来源 */
/** 会话值缓存键,插值唯一 ID */
private static final String CACHE_KEY = "CAPTCHA:%s";
/** 绘制字符,移除 O,o,I,l */
@@ -49,20 +53,22 @@ public class CaptchaManager {
@Value("${spring.profiles.active}")
private String env;
private final Redis<String, String> redisCaptcha;
public BufferedImage generate(CaptchaFrom from, int width, int height) throws InterruptedException {
return generate(from, 4, width, height);
public Result generate(int width, int height) {
return generate(width, height, 4);
}
/**
* 生成并缓存验证码
*
* @param from 来自模块
* @param width 宽度
* @param height 高度
* @param length 长度
* @return 图片流
*/
public BufferedImage generate(CaptchaFrom from, int length, int width, int height) throws InterruptedException {
public Result generate(int width, int height, int length) {
long lockTime = 1000;
// 加锁
String lockKey = LOCK_KEY.formatted(TimiSpring.getSession().getId());
Long lockAt = TimiSpring.getSessionAttr(lockKey, Long.class);
@@ -70,17 +76,22 @@ public class CaptchaManager {
TimiSpring.setSessionAttr(lockKey, Time.now());
if (lockAt != null) {
long diff = Time.now() - lockAt;
if (diff < LOCK_TIME) {
if (diff < lockTime) {
// 限制频率
synchronized (this) {
wait(LOCK_TIME - diff);
try {
wait(lockTime - diff);
} catch (InterruptedException e) {
log.error("lock error", e);
throw new RuntimeException(e);
}
}
}
}
int fontWidth = width / 4;
int fontSize = (int) (height * .8);
int yOffset = (int) (height * .2);
int fontWidth = (int) (width * .9 / 4);
int fontSize = (int) (height * .7);
int yOffset = (int) (height * .4);
StringBuilder value = new StringBuilder();
// 图片流
@@ -139,9 +150,19 @@ public class CaptchaManager {
g.draw(curve);
}
}
// 写入缓存
TimiSpring.setSessionAttr(CACHE_KEY.formatted(from), value.toString());
return image;
try {
String uniqueId = UUID.randomUUID().toString();
// 写入缓存
redisCaptcha.set(CACHE_KEY.formatted(uniqueId), value.toString(), Time.parseToMS("3m"));
// 返回
ByteArrayOutputStream os = new ByteArrayOutputStream();
ImageIO.write(image, "png", os);
return new Result(uniqueId, "data:image/png;base64," + Encoder.base64(os.toByteArray()));
} catch (IOException e) {
throw new TimiException(TimiCode.ERROR);
}
}
/** @return 错误回调图像 */
@@ -158,30 +179,40 @@ public class CaptchaManager {
}
/**
* <p>验证。通过验证不抛出任何异常也不返回任何内容,否则抛出相应异常
* <p>会验证非空和期限
* <p>测试环境总是通过验证
* 验证
*
* @param captcha 提交的验证码
* @param from 来自模块
* <ul>
* <li>通过验证不抛出任何异常也不返回任何内容,否则抛出相应异常</li>
* <li>会验证非空和期限</li>
* </ul>
*
* @param uniqueId 唯一识别 ID
* @param value 提交的验证码
* @throws TimiException 验证异常
*/
public void test(String captcha, String from) {
if (TimiJava.isEmpty(captcha)) {
throw new TimiException(TimiCode.ARG_MISS).msgKey("captcha.miss");
public void verify(String uniqueId, String value) {
if (TimiJava.isEmpty(value)) {
throw new TimiException(TimiCode.ARG_MISS, "not found value");
}
if (env.startsWith("dev")) {
return;
}
// Session 验证
String sessionCaptcha = TimiSpring.getSessionAttrAsString(CACHE_KEY.formatted(from));
if (TimiJava.isEmpty(sessionCaptcha)) {
String cacheKey = CACHE_KEY.formatted(uniqueId);
String cacheValue = redisCaptcha.get(cacheKey);
if (TimiJava.isEmpty(cacheValue)) {
throw new TimiException(TimiCode.ARG_EXPIRED).msgKey("captcha.expire");
}
if (!captcha.trim().equalsIgnoreCase(sessionCaptcha)) {
if (!value.trim().equalsIgnoreCase(cacheValue)) {
throw new TimiException(TimiCode.RESULT_BAD).msgKey("captcha.error");
}
// 清除缓存
TimiSpring.removeSessionAttr(CACHE_KEY.formatted(from));
redisCaptcha.destroy(cacheKey);
}
/**
* @author 夜雨
* @since 2025-10-30 14:48
*/
public record Result(String id, String data) {
}
}