Commit 7d1946d91e1115f4d3cb79e76524eb48472b6b91
1 parent
3102fc1f
reactor:【system 系统功能】优化“文字验证码”的代码
Showing
2 changed files
with
33 additions
and
48 deletions
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 | ... | ... |