diff --git a/pom.xml b/pom.xml
index 26116b3..5c78b5f 100644
--- a/pom.xml
+++ b/pom.xml
@@ -42,6 +42,12 @@
+ com.github.penggle
+ kaptcha
+ 2.3.2
+
+
+
org.apache.poi
poi-ooxml
5.2.3
diff --git a/src/main/java/com/example/demo/config/KaptchaConfig.java b/src/main/java/com/example/demo/config/KaptchaConfig.java
new file mode 100644
index 0000000..b505acd
--- /dev/null
+++ b/src/main/java/com/example/demo/config/KaptchaConfig.java
@@ -0,0 +1,31 @@
+// com.example.demo.config.KaptchaConfig.java
+package com.example.demo.config;
+
+import com.google.code.kaptcha.impl.DefaultKaptcha;
+import com.google.code.kaptcha.util.Config;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+
+import java.util.Properties;
+
+@Configuration
+public class KaptchaConfig {
+
+ @Bean
+ public DefaultKaptcha defaultKaptcha() {
+ DefaultKaptcha kaptcha = new DefaultKaptcha();
+ Properties properties = new Properties();
+ properties.setProperty("kaptcha.image.width", "130");
+ properties.setProperty("kaptcha.image.height", "45");
+ properties.setProperty("kaptcha.textproducer.char.length", "4");
+ properties.setProperty("kaptcha.textproducer.font.size", "35");
+ properties.setProperty("kaptcha.textproducer.font.color", "black");
+ properties.setProperty("kaptcha.textproducer.char.space", "5");
+ properties.setProperty("kaptcha.obscurificator.impl", "com.google.code.kaptcha.impl.WaterRipple");
+ properties.setProperty("kaptcha.noise.impl", "com.google.code.kaptcha.impl.DefaultNoise");
+
+ kaptcha.setConfig(new Config(properties));
+ return kaptcha;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/example/demo/controller/coin/AdminController.java b/src/main/java/com/example/demo/controller/coin/AdminController.java
index 18ffba2..89e5388 100644
--- a/src/main/java/com/example/demo/controller/coin/AdminController.java
+++ b/src/main/java/com/example/demo/controller/coin/AdminController.java
@@ -12,6 +12,7 @@ import com.example.demo.service.coin.TranslationService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.web.bind.annotation.*;
@@ -37,6 +38,8 @@ public class AdminController {
private LanguageTranslationUtil languageTranslationUtil;
@Autowired
private TranslationService translationService;
+ @Autowired
+ private StringRedisTemplate redisTemplate;
@PostMapping("/test")
public void testGetAdmin() {
@@ -48,23 +51,50 @@ public class AdminController {
@Log("用户登录")
@PostMapping("/login")
public Result login(@RequestBody Admin admin, @RequestHeader(defaultValue = "zh_CN") String lang) {
-
try {
+ // ====== 【新增】验证码校验逻辑 ======
+ if (admin.getCaptcha() == null || admin.getUuid() == null) {
+ String errorMsg = "验证码或验证码ID缺失";
+ String translatedErrorMsg = languageTranslationUtil.translate(errorMsg, lang);
+ return Result.error(translatedErrorMsg);
+ }
+
+ String cacheCode = redisTemplate.opsForValue().get("CAPTCHA:" + admin.getUuid());
+ if (cacheCode == null) {
+ String errorMsg = "验证码已过期,请重新获取";
+ String translatedErrorMsg = languageTranslationUtil.translate(errorMsg, lang);
+ return Result.error(translatedErrorMsg);
+ }
+
+ if (!cacheCode.equalsIgnoreCase(admin.getCaptcha())) {
+ String errorMsg = "验证码错误";
+ String translatedErrorMsg = languageTranslationUtil.translate(errorMsg, lang);
+ return Result.error(translatedErrorMsg);
+ }
+ // ====== 验证码校验结束 ======
+
// 解析语言代码
String languageCode = parseLanguageCode(lang);
- // 如果不是中文环境,将输入的翻译字段转换为中文简体
if (!"zh".equalsIgnoreCase(languageCode) && !"zh_cn".equalsIgnoreCase(languageCode)) {
convertLoginFieldsToChinese(admin, languageCode);
}
+ // 执行登录(此时 admin 包含用户名、密码)
admin = adminService.login(admin);
+
+ // 登录成功后,删除已使用的验证码(防止重放)
+ redisTemplate.delete("CAPTCHA:" + admin.getUuid());
+
+ // 生成 token
String token = JWTUtil.createJWT(admin);
- // 对返回的管理员信息进行多语言转换
+
+ // 多语言转换
translateAdminInfoForLogin(admin, lang);
admin.setPassword(null);
return Result.success(token, admin);
+
} catch (Exception e) {
e.printStackTrace();
log.error(e.getMessage());
diff --git a/src/main/java/com/example/demo/controller/coin/CaptchaController.java b/src/main/java/com/example/demo/controller/coin/CaptchaController.java
new file mode 100644
index 0000000..29f312c
--- /dev/null
+++ b/src/main/java/com/example/demo/controller/coin/CaptchaController.java
@@ -0,0 +1,49 @@
+// com.example.demo.controller.CaptchaController.java
+package com.example.demo.controller.coin;
+
+import com.google.code.kaptcha.Producer;
+import jakarta.servlet.http.HttpServletResponse;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.StringRedisTemplate;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.imageio.ImageIO;
+import java.awt.image.BufferedImage;
+import java.io.IOException;
+import java.util.concurrent.TimeUnit;
+
+@RestController
+public class CaptchaController {
+
+ @Autowired
+ private Producer kaptchaProducer;
+
+ @Autowired
+ private StringRedisTemplate redisTemplate;
+
+ /**
+ * 获取图形验证码
+ * @param uuid 前端生成的唯一标识,用于关联验证码
+ */
+ @GetMapping("/captcha")
+ public void captcha(@RequestParam String uuid, HttpServletResponse response) throws IOException {
+ if (uuid == null || uuid.trim().isEmpty()) {
+ response.sendError(HttpServletResponse.SC_BAD_REQUEST, "uuid is required");
+ return;
+ }
+
+ // 生成验证码文本和图片
+ String code = kaptchaProducer.createText();
+ BufferedImage image = kaptchaProducer.createImage(code);
+
+ // 存入 Redis,5分钟过期
+ redisTemplate.opsForValue().set("CAPTCHA:" + uuid, code, 5, TimeUnit.MINUTES);
+
+ // 输出图片
+ response.setHeader("Cache-Control", "no-store");
+ response.setContentType("image/jpeg");
+ ImageIO.write(image, "jpg", response.getOutputStream());
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/example/demo/domain/entity/Admin.java b/src/main/java/com/example/demo/domain/entity/Admin.java
index 3630b1b..6355b1f 100644
--- a/src/main/java/com/example/demo/domain/entity/Admin.java
+++ b/src/main/java/com/example/demo/domain/entity/Admin.java
@@ -3,6 +3,7 @@ package com.example.demo.domain.entity;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import jakarta.validation.constraints.NotBlank;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.security.core.GrantedAuthority;
@@ -34,12 +35,17 @@ public class Admin implements UserDetails, Serializable {
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Shanghai")
private Date createTime; // 创建时间
+ @NotBlank(message = "验证码不能为空")
+ private String captcha;
+ @NotBlank(message = "验证码ID不能为空")
+ private String uuid; // 用于从 Redis 中取验证码
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Shanghai")
private Date updateTime; // 更新时间
private Integer roleId;
+
@Override
@JsonIgnore
public Collection extends GrantedAuthority> getAuthorities() {
diff --git a/src/main/java/com/example/demo/security/SecurityConfig.java b/src/main/java/com/example/demo/security/SecurityConfig.java
index e7c0ecb..1530afa 100644
--- a/src/main/java/com/example/demo/security/SecurityConfig.java
+++ b/src/main/java/com/example/demo/security/SecurityConfig.java
@@ -61,6 +61,7 @@ public class SecurityConfig {
.requestMatchers( HttpMethod.POST,
// 用户不登录就可以访问的路径
"/admin/login","/upload/**","/detailY/ERP","/home/java/haiwaiyanfa/gold1/**","/home/java/haiwaiyanfa/**","/statistics/**","/Mysql/**","/Temporary/**","/cashCollection/syncToCashRecord").permitAll()
+ .requestMatchers(HttpMethod.GET, "/captcha").permitAll()
.requestMatchers(
"/error","alipay/**","/upload/**","/home/java/haiwaiyanfa/gold1/**","/home/java/haiwaiyanfa/**"
).permitAll()
diff --git a/src/main/resources/cashMapper/CashCollectionMapper.xml b/src/main/resources/cashMapper/CashCollectionMapper.xml
index 7f25eef..4b4a0d1 100644
--- a/src/main/resources/cashMapper/CashCollectionMapper.xml
+++ b/src/main/resources/cashMapper/CashCollectionMapper.xml
@@ -422,8 +422,8 @@
update cash_record_collection
SET
received_time=#{availableOn},
- payment_currency='2',
- received_currency='2',
+ payment_currency='3',
+ received_currency='3',
payment_amount=#{amount},
received_amount=#{net},
handling_charge=#{fee},