Commit 7d1946d91e1115f4d3cb79e76524eb48472b6b91

Authored by YunaiV
1 parent 3102fc1f

reactor:【system 系统功能】优化“文字验证码”的代码

yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/captcha/core/PictureWordCaptchaServiceImpl.java
1 1 package cn.iocoder.yudao.module.system.framework.captcha.core;
2 2  
3   -import com.anji.captcha.model.common.CaptchaTypeEnum;
4 3 import com.anji.captcha.model.common.RepCodeEnum;
5 4 import com.anji.captcha.model.common.ResponseModel;
6 5 import com.anji.captcha.model.vo.CaptchaVO;
... ... @@ -10,12 +9,12 @@ import com.anji.captcha.util.AESUtil;
10 9 import com.anji.captcha.util.ImageUtils;
11 10 import com.anji.captcha.util.RandomUtils;
12 11 import org.apache.commons.lang3.StringUtils;
  12 +import cn.hutool.core.util.RandomUtil;
13 13  
14 14 import java.awt.*;
15 15 import java.awt.geom.AffineTransform;
16 16 import java.awt.image.BufferedImage;
17 17 import java.util.Properties;
18   -import java.util.Random;
19 18  
20 19 /**
21 20 * 图片文字验证码
... ... @@ -25,11 +24,18 @@ import java.util.Random;
25 24 */
26 25 public class PictureWordCaptchaServiceImpl extends AbstractCaptchaService {
27 26  
  27 + /**
  28 + * 验证码的基础字符
  29 + */
  30 + private static final String CHARACTERS = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789";
  31 + /**
  32 + * 验证码长度
  33 + */
  34 + private static final Integer LENGTH = 4;
  35 +
28 36 private static final int WIDTH = 120;
29 37 private static final int HEIGHT = 40;
30 38 private static final int LINES = 10;
31   - private static final Random RANDOM = new Random();
32   - private static final String CHARACTERS = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789";
33 39  
34 40 @Override
35 41 public void init(Properties config) {
... ... @@ -44,18 +50,14 @@ public class PictureWordCaptchaServiceImpl extends AbstractCaptchaService {
44 50 @Override
45 51 public String captchaType() {
46 52 return "pictureWord";
47   -// return CaptchaTypeEnum.PICTURE_WORD.getCodeValue();
48 53 }
49 54  
50 55 @Override
51 56 public ResponseModel get(CaptchaVO captchaVO) {
52   - String text = generateRandomText(4);
53   -
  57 + String text = generateRandomText(LENGTH);
54 58 CaptchaVO imageData = getImageData(text);
55   -
56   - // pointJson不传到前端,只做后端校验,测试时放开
  59 + // pointJson 不传到前端,只做后端校验,测试时放开
57 60 // imageData.setPointJson(text);
58   -
59 61 return ResponseModel.successData(imageData);
60 62 }
61 63  
... ... @@ -75,22 +77,22 @@ public class PictureWordCaptchaServiceImpl extends AbstractCaptchaService {
75 77 String codeValue = CaptchaServiceFactory.getCache(cacheType).get(codeKey);
76 78 String code = getCodeByCodeValue(codeValue);
77 79 String secretKey = getSecretKeyByCodeValue(codeValue);
78   - //验证码只用一次,即刻失效
  80 + // 验证码只用一次,即刻失效
79 81 CaptchaServiceFactory.getCache(cacheType).delete(codeKey);
80 82  
81   - // 用户输入的验证码(CaptchaVO中没有预留字段,暂时用pointJson,无需加解密)
  83 + // 用户输入的验证码(CaptchaVO 中 没有预留字段,暂时用 pointJson 无需加解密)
82 84 String userCode = captchaVO.getPointJson();
83 85 if (!StringUtils.equalsIgnoreCase(code, userCode)) {
84 86 afterValidateFail(captchaVO);
85 87 return ResponseModel.errorMsg(RepCodeEnum.API_CAPTCHA_COORDINATE_ERROR);
86 88 }
87 89  
88   - //校验成功,将信息存入缓存
  90 + // 校验成功,将信息存入缓存
89 91 String value;
90 92 try {
91 93 value = AESUtil.aesEncrypt(captchaVO.getToken().concat("---").concat(userCode), secretKey);
92 94 } catch (Exception e) {
93   - logger.error("AES加密失败", e);
  95 + logger.error("AES 加密失败", e);
94 96 afterValidateFail(captchaVO);
95 97 return ResponseModel.errorMsg(e.getMessage());
96 98 }
... ... @@ -112,7 +114,7 @@ public class PictureWordCaptchaServiceImpl extends AbstractCaptchaService {
112 114 if (!CaptchaServiceFactory.getCache(cacheType).exists(codeKey)) {
113 115 return ResponseModel.errorMsg(RepCodeEnum.API_CAPTCHA_INVALID);
114 116 }
115   - //二次校验取值后,即刻失效
  117 + // 二次校验取值后,即刻失效
116 118 CaptchaServiceFactory.getCache(cacheType).delete(codeKey);
117 119 } catch (Exception e) {
118 120 logger.error("验证码解析失败", e);
... ... @@ -130,42 +132,35 @@ public class PictureWordCaptchaServiceImpl extends AbstractCaptchaService {
130 132 // 设置背景色
131 133 g.setColor(getRandomColor(200, 250));
132 134 g.fillRect(0, 0, WIDTH, HEIGHT);
133   -
134 135 // 绘制干扰线
135 136 for (int i = 0; i < LINES; i++) {
136 137 g.setColor(getRandomColor(100, 200));
137   - int x1 = RANDOM.nextInt(WIDTH);
138   - int y1 = RANDOM.nextInt(HEIGHT);
139   - int x2 = RANDOM.nextInt(WIDTH);
140   - int y2 = RANDOM.nextInt(HEIGHT);
  138 + int x1 = RandomUtil.randomInt(WIDTH);
  139 + int y1 = RandomUtil.randomInt(HEIGHT);
  140 + int x2 = RandomUtil.randomInt(WIDTH);
  141 + int y2 = RandomUtil.randomInt(HEIGHT);
141 142 g.drawLine(x1, y1, x2, y2);
142 143 }
143   -
144 144 // 设置字体
145 145 g.setFont(new Font("Arial", Font.BOLD, 24));
146   -
147 146 // 绘制验证码文本
148 147 for (int i = 0; i < text.length(); i++) {
149 148 g.setColor(getRandomColor(20, 130));
150   -
151 149 // 文字旋转
152 150 AffineTransform affineTransform = new AffineTransform();
153 151 int x = 20 + i * 20;
154   - int y = 24 + RANDOM.nextInt(8);
  152 + int y = 24 + RandomUtil.randomInt(8);
155 153 // 旋转范围 -45 ~ 45
156   - affineTransform.setToRotation(Math.toRadians(RandomUtils.getRandomInt(-45, 45)), x, y);
  154 + affineTransform.setToRotation(Math.toRadians(RandomUtil.randomInt(-45, 45)), x, y);
157 155 g.setTransform(affineTransform);
158   -
159 156 g.drawString(text.charAt(i) + "", x, y);
160 157 }
161   -
162 158 // 添加噪点
163 159 for (int i = 0; i < 100; i++) {
164   - int x = RANDOM.nextInt(WIDTH);
165   - int y = RANDOM.nextInt(HEIGHT);
  160 + int x = RandomUtil.randomInt(WIDTH);
  161 + int y = RandomUtil.randomInt(HEIGHT);
166 162 image.setRGB(x, y, getRandomColor(0, 255).getRGB());
167 163 }
168   -
169 164 g.dispose();
170 165  
171 166 String secretKey = null;
... ... @@ -177,7 +172,7 @@ public class PictureWordCaptchaServiceImpl extends AbstractCaptchaService {
177 172 dataVO.setOriginalImageBase64(ImageUtils.getImageToBase64Str(image).replaceAll("\r|\n", ""));
178 173 dataVO.setToken(RandomUtils.getUUID());
179 174 // dataVO.setSecretKey(secretKey);
180   - //将坐标信息存入redis中
  175 + // 将坐标信息存入 redis 中
181 176 String codeKey = String.format(REDIS_CAPTCHA_KEY, dataVO.getToken());
182 177 CaptchaServiceFactory.getCache(cacheType).set(codeKey, getCodeValue(text, secretKey), EXPIRESIN_SECONDS);
183 178 return dataVO;
... ... @@ -196,14 +191,11 @@ public class PictureWordCaptchaServiceImpl extends AbstractCaptchaService {
196 191 }
197 192  
198 193 private Color getRandomColor(int min, int max) {
199   - if (min > max) {
200   - int temp = min;
201   - min = max;
202   - max = temp;
203   - }
204   - int r = min + RANDOM.nextInt(max - min);
205   - int g = min + RANDOM.nextInt(max - min);
206   - int b = min + RANDOM.nextInt(max - min);
  194 + int minVal = Math.min(min, max);
  195 + int maxVal = Math.max(min, max);
  196 + int r = RandomUtil.randomInt(minVal, maxVal);
  197 + int g = RandomUtil.randomInt(minVal, maxVal);
  198 + int b = RandomUtil.randomInt(minVal, maxVal);
207 199 return new Color(r, g, b);
208 200 }
209 201  
... ... @@ -212,16 +204,9 @@ public class PictureWordCaptchaServiceImpl extends AbstractCaptchaService {
212 204 *
213 205 * @param length 长度
214 206 * @return {@link String}
215   - * @author Rex
216   - * @since 2025/6/26 15:20
217 207 */
218 208 public static String generateRandomText(int length) {
219   - StringBuilder sb = new StringBuilder();
220   - for (int i = 0; i < length; i++) {
221   - int index = RANDOM.nextInt(CHARACTERS.length());
222   - sb.append(CHARACTERS.charAt(index));
223   - }
224   - return sb.toString();
  209 + return RandomUtil.randomString(CHARACTERS, length);
225 210 }
226 211  
227 212 }
228 213 \ No newline at end of file
... ...
yudao-server/src/main/resources/application.yaml
... ... @@ -107,7 +107,7 @@ aj:
107 107 cache-type: redis # 缓存 local/redis...
108 108 cache-number: 1000 # local 缓存的阈值,达到这个值,清除缓存
109 109 timing-clear: 180 # local定时清除过期缓存(单位秒),设置为0代表不执行
110   - type: blockPuzzle # 验证码类型 default两种都实例化。 blockPuzzle 滑块拼图 clickWord 文字点选
  110 + type: blockPuzzle # 验证码类型 default 三种都实例化。blockPuzzle 滑块拼图、clickWord 文字点选、pictureWord 文本输入
111 111 water-mark: 芋道源码 # 右下角水印文字(我的水印),可使用 https://tool.chinaz.com/tools/unicode.aspx 中文转 Unicode,Linux 可能需要转 unicode
112 112 interference-options: 0 # 滑动干扰项(0/1/2)
113 113 req-frequency-limit-enable: false # 接口请求次数一分钟限制是否开启 true|false
... ...