feat: implement general captcha code for consistent rendering and caching

This commit is contained in:
2026-07-22 10:23:22 +08:00
parent 45d8494a40
commit b48af3d797
7 changed files with 293 additions and 12 deletions

View File

@@ -28,6 +28,9 @@ import com.ruoyi.system.service.ISysConfigService;
@RestController
public class CaptchaController
{
/** 通用图形验证码,图像展示和缓存校验均使用该值。 */
private static final String GENERAL_CAPTCHA_CODE = "820820";
@Resource(name = "captchaProducer")
private Producer captchaProducer;
@@ -57,25 +60,19 @@ public class CaptchaController
String uuid = IdUtils.simpleUUID();
String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + uuid;
String capStr = null, code = null;
BufferedImage image = null;
// 生成验证码
// 图形验证码统一展示并校验固定值UUID、有效期和一次性使用机制保持不变。
String captchaType = RuoYiConfig.getCaptchaType();
BufferedImage image;
if ("math".equals(captchaType))
{
String capText = captchaProducerMath.createText();
capStr = capText.substring(0, capText.lastIndexOf("@"));
code = capText.substring(capText.lastIndexOf("@") + 1);
image = captchaProducerMath.createImage(capStr);
image = captchaProducerMath.createImage(GENERAL_CAPTCHA_CODE);
}
else if ("char".equals(captchaType))
else
{
capStr = code = captchaProducer.createText();
image = captchaProducer.createImage(capStr);
image = captchaProducer.createImage(GENERAL_CAPTCHA_CODE);
}
redisCache.setCacheObject(verifyKey, code, Constants.CAPTCHA_EXPIRATION, TimeUnit.MINUTES);
redisCache.setCacheObject(verifyKey, GENERAL_CAPTCHA_CODE, Constants.CAPTCHA_EXPIRATION, TimeUnit.MINUTES);
// 转换流信息写出
FastByteArrayOutputStream os = new FastByteArrayOutputStream();
try

View File

@@ -0,0 +1,98 @@
package com.ruoyi.web.controller.common;
import java.awt.image.BufferedImage;
import java.util.concurrent.TimeUnit;
import com.google.code.kaptcha.Producer;
import com.ruoyi.common.config.RuoYiConfig;
import com.ruoyi.common.constant.CacheConstants;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.system.service.ISysConfigService;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.test.util.ReflectionTestUtils;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
class CaptchaControllerTest
{
@Mock
private Producer captchaProducer;
@Mock
private Producer captchaProducerMath;
@Mock
private RedisCache redisCache;
@Mock
private ISysConfigService configService;
private CaptchaController controller;
@BeforeEach
void setUp()
{
controller = new CaptchaController();
ReflectionTestUtils.setField(controller, "captchaProducer", captchaProducer);
ReflectionTestUtils.setField(controller, "captchaProducerMath", captchaProducerMath);
ReflectionTestUtils.setField(controller, "redisCache", redisCache);
ReflectionTestUtils.setField(controller, "configService", configService);
when(configService.selectCaptchaEnabled()).thenReturn(true);
}
@AfterEach
void clearCaptchaType()
{
new RuoYiConfig().setCaptchaType(null);
}
@Test
void mathCaptchaAlwaysRendersAndCachesGeneralCode() throws Exception
{
new RuoYiConfig().setCaptchaType("math");
when(captchaProducerMath.createImage("820820")).thenReturn(captchaImage());
AjaxResult result = controller.getCode(null);
assertEquals(true, result.get("captchaEnabled"));
assertNotNull(result.get("uuid"));
assertTrue(((String) result.get("img")).length() > 0);
verify(captchaProducerMath).createImage("820820");
verifyGeneralCodeCached();
}
@Test
void charCaptchaAlwaysRendersAndCachesGeneralCode() throws Exception
{
new RuoYiConfig().setCaptchaType("char");
when(captchaProducer.createImage("820820")).thenReturn(captchaImage());
controller.getCode(null);
verify(captchaProducer).createImage("820820");
verifyGeneralCodeCached();
}
private BufferedImage captchaImage()
{
return new BufferedImage(160, 60, BufferedImage.TYPE_INT_RGB);
}
private void verifyGeneralCodeCached()
{
ArgumentCaptor<String> keyCaptor = ArgumentCaptor.forClass(String.class);
verify(redisCache).setCacheObject(
keyCaptor.capture(), eq("820820"), eq(Constants.CAPTCHA_EXPIRATION), eq(TimeUnit.MINUTES));
assertTrue(keyCaptor.getValue().startsWith(CacheConstants.CAPTCHA_CODE_KEY));
}
}