Commit df3ef54b41466f34dfda99313fdc9fb7b4de81b3
1 parent
1e40e60d
验证码类型支持(数组计算、字符验证)
Showing
7 changed files
with
228 additions
and
21 deletions
pom.xml
... | ... | @@ -21,6 +21,7 @@ |
21 | 21 | <druid.version>1.1.14</druid.version> |
22 | 22 | <bitwalker.version>1.19</bitwalker.version> |
23 | 23 | <swagger.version>2.9.2</swagger.version> |
24 | + <kaptcha.version>2.3.2</kaptcha.version> | |
24 | 25 | <pagehelper.boot.version>1.2.5</pagehelper.boot.version> |
25 | 26 | <fastjson.version>1.2.70</fastjson.version> |
26 | 27 | <oshi.version>3.9.1</oshi.version> |
... | ... | @@ -137,6 +138,13 @@ |
137 | 138 | <artifactId>jjwt</artifactId> |
138 | 139 | <version>${jwt.version}</version> |
139 | 140 | </dependency> |
141 | + | |
142 | + <!--验证码 --> | |
143 | + <dependency> | |
144 | + <groupId>com.github.penggle</groupId> | |
145 | + <artifactId>kaptcha</artifactId> | |
146 | + <version>${kaptcha.version}</version> | |
147 | + </dependency> | |
140 | 148 | |
141 | 149 | <!-- 定时任务--> |
142 | 150 | <dependency> |
... | ... |
ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/CaptchaController.java
1 | 1 | package com.ruoyi.web.controller.common; |
2 | 2 | |
3 | -import java.io.ByteArrayOutputStream; | |
3 | +import java.awt.image.BufferedImage; | |
4 | 4 | import java.io.IOException; |
5 | 5 | import java.util.concurrent.TimeUnit; |
6 | +import javax.annotation.Resource; | |
7 | +import javax.imageio.ImageIO; | |
6 | 8 | import javax.servlet.http.HttpServletResponse; |
7 | 9 | import org.springframework.beans.factory.annotation.Autowired; |
10 | +import org.springframework.beans.factory.annotation.Value; | |
11 | +import org.springframework.util.FastByteArrayOutputStream; | |
8 | 12 | import org.springframework.web.bind.annotation.GetMapping; |
9 | 13 | import org.springframework.web.bind.annotation.RestController; |
14 | +import com.google.code.kaptcha.Producer; | |
10 | 15 | import com.ruoyi.common.constant.Constants; |
11 | 16 | import com.ruoyi.common.core.domain.AjaxResult; |
12 | 17 | import com.ruoyi.common.core.redis.RedisCache; |
13 | -import com.ruoyi.common.utils.VerifyCodeUtils; | |
14 | 18 | import com.ruoyi.common.utils.sign.Base64; |
15 | 19 | import com.ruoyi.common.utils.uuid.IdUtils; |
16 | 20 | |
... | ... | @@ -22,8 +26,18 @@ import com.ruoyi.common.utils.uuid.IdUtils; |
22 | 26 | @RestController |
23 | 27 | public class CaptchaController |
24 | 28 | { |
29 | + @Resource(name = "captchaProducer") | |
30 | + private Producer captchaProducer; | |
31 | + | |
32 | + @Resource(name = "captchaProducerMath") | |
33 | + private Producer captchaProducerMath; | |
34 | + | |
25 | 35 | @Autowired |
26 | 36 | private RedisCache redisCache; |
37 | + | |
38 | + // 验证码类型 | |
39 | + @Value("${ruoyi.captchaType}") | |
40 | + private String captchaType; | |
27 | 41 | |
28 | 42 | /** |
29 | 43 | * 生成验证码 |
... | ... | @@ -31,32 +45,42 @@ public class CaptchaController |
31 | 45 | @GetMapping("/captchaImage") |
32 | 46 | public AjaxResult getCode(HttpServletResponse response) throws IOException |
33 | 47 | { |
34 | - // 生成随机字串 | |
35 | - String verifyCode = VerifyCodeUtils.generateVerifyCode(4); | |
36 | - // 唯一标识 | |
48 | + // 保存验证码信息 | |
37 | 49 | String uuid = IdUtils.simpleUUID(); |
38 | 50 | String verifyKey = Constants.CAPTCHA_CODE_KEY + uuid; |
39 | 51 | |
40 | - redisCache.setCacheObject(verifyKey, verifyCode, Constants.CAPTCHA_EXPIRATION, TimeUnit.MINUTES); | |
41 | - // 生成图片 | |
42 | - int w = 111, h = 36; | |
43 | - ByteArrayOutputStream stream = new ByteArrayOutputStream(); | |
44 | - VerifyCodeUtils.outputImage(w, h, stream, verifyCode); | |
45 | - try | |
52 | + String capStr = null, code = null; | |
53 | + BufferedImage image = null; | |
54 | + | |
55 | + // 生成验证码 | |
56 | + if ("math".equals(captchaType)) | |
46 | 57 | { |
47 | - AjaxResult ajax = AjaxResult.success(); | |
48 | - ajax.put("uuid", uuid); | |
49 | - ajax.put("img", Base64.encode(stream.toByteArray())); | |
50 | - return ajax; | |
58 | + String capText = captchaProducerMath.createText(); | |
59 | + capStr = capText.substring(0, capText.lastIndexOf("@")); | |
60 | + code = capText.substring(capText.lastIndexOf("@") + 1); | |
61 | + image = captchaProducerMath.createImage(capStr); | |
51 | 62 | } |
52 | - catch (Exception e) | |
63 | + else if ("char".equals(captchaType)) | |
53 | 64 | { |
54 | - e.printStackTrace(); | |
55 | - return AjaxResult.error(e.getMessage()); | |
65 | + capStr = code = captchaProducer.createText(); | |
66 | + image = captchaProducer.createImage(capStr); | |
56 | 67 | } |
57 | - finally | |
68 | + | |
69 | + redisCache.setCacheObject(verifyKey, code, Constants.CAPTCHA_EXPIRATION, TimeUnit.MINUTES); | |
70 | + // 转换流信息写出 | |
71 | + FastByteArrayOutputStream os = new FastByteArrayOutputStream(); | |
72 | + try | |
58 | 73 | { |
59 | - stream.close(); | |
74 | + ImageIO.write(image, "jpg", os); | |
60 | 75 | } |
76 | + catch (IOException e) | |
77 | + { | |
78 | + return AjaxResult.error(e.getMessage()); | |
79 | + } | |
80 | + | |
81 | + AjaxResult ajax = AjaxResult.success(); | |
82 | + ajax.put("uuid", uuid); | |
83 | + ajax.put("img", Base64.encode(os.toByteArray())); | |
84 | + return ajax; | |
61 | 85 | } |
62 | 86 | } |
... | ... |
ruoyi-admin/src/main/resources/application.yml
ruoyi-framework/pom.xml
... | ... | @@ -35,6 +35,18 @@ |
35 | 35 | <artifactId>druid-spring-boot-starter</artifactId> |
36 | 36 | </dependency> |
37 | 37 | |
38 | + <!-- 验证码 --> | |
39 | + <dependency> | |
40 | + <groupId>com.github.penggle</groupId> | |
41 | + <artifactId>kaptcha</artifactId> | |
42 | + <exclusions> | |
43 | + <exclusion> | |
44 | + <artifactId>javax.servlet-api</artifactId> | |
45 | + <groupId>javax.servlet</groupId> | |
46 | + </exclusion> | |
47 | + </exclusions> | |
48 | + </dependency> | |
49 | + | |
38 | 50 | <!-- 获取系统信息 --> |
39 | 51 | <dependency> |
40 | 52 | <groupId>com.github.oshi</groupId> |
... | ... |
ruoyi-framework/src/main/java/com/ruoyi/framework/config/CaptchaConfig.java
0 → 100644
1 | +package com.ruoyi.framework.config; | |
2 | + | |
3 | +import java.util.Properties; | |
4 | +import org.springframework.context.annotation.Bean; | |
5 | +import org.springframework.context.annotation.Configuration; | |
6 | +import com.google.code.kaptcha.impl.DefaultKaptcha; | |
7 | +import com.google.code.kaptcha.util.Config; | |
8 | +import static com.google.code.kaptcha.Constants.*; | |
9 | + | |
10 | +/** | |
11 | + * 验证码配置 | |
12 | + * | |
13 | + * @author ruoyi | |
14 | + */ | |
15 | +@Configuration | |
16 | +public class CaptchaConfig | |
17 | +{ | |
18 | + @Bean(name = "captchaProducer") | |
19 | + public DefaultKaptcha getKaptchaBean() | |
20 | + { | |
21 | + DefaultKaptcha defaultKaptcha = new DefaultKaptcha(); | |
22 | + Properties properties = new Properties(); | |
23 | + // 是否有边框 默认为true 我们可以自己设置yes,no | |
24 | + properties.setProperty(KAPTCHA_BORDER, "yes"); | |
25 | + // 验证码文本字符颜色 默认为Color.BLACK | |
26 | + properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_COLOR, "black"); | |
27 | + // 验证码图片宽度 默认为200 | |
28 | + properties.setProperty(KAPTCHA_IMAGE_WIDTH, "160"); | |
29 | + // 验证码图片高度 默认为50 | |
30 | + properties.setProperty(KAPTCHA_IMAGE_HEIGHT, "60"); | |
31 | + // 验证码文本字符大小 默认为40 | |
32 | + properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_SIZE, "38"); | |
33 | + // KAPTCHA_SESSION_KEY | |
34 | + properties.setProperty(KAPTCHA_SESSION_CONFIG_KEY, "kaptchaCode"); | |
35 | + // 验证码文本字符长度 默认为5 | |
36 | + properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_LENGTH, "4"); | |
37 | + // 验证码文本字体样式 默认为new Font("Arial", 1, fontSize), new Font("Courier", 1, fontSize) | |
38 | + properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_NAMES, "Arial,Courier"); | |
39 | + // 图片样式 水纹com.google.code.kaptcha.impl.WaterRipple 鱼眼com.google.code.kaptcha.impl.FishEyeGimpy 阴影com.google.code.kaptcha.impl.ShadowGimpy | |
40 | + properties.setProperty(KAPTCHA_OBSCURIFICATOR_IMPL, "com.google.code.kaptcha.impl.ShadowGimpy"); | |
41 | + Config config = new Config(properties); | |
42 | + defaultKaptcha.setConfig(config); | |
43 | + return defaultKaptcha; | |
44 | + } | |
45 | + | |
46 | + @Bean(name = "captchaProducerMath") | |
47 | + public DefaultKaptcha getKaptchaBeanMath() | |
48 | + { | |
49 | + DefaultKaptcha defaultKaptcha = new DefaultKaptcha(); | |
50 | + Properties properties = new Properties(); | |
51 | + // 是否有边框 默认为true 我们可以自己设置yes,no | |
52 | + properties.setProperty(KAPTCHA_BORDER, "yes"); | |
53 | + // 边框颜色 默认为Color.BLACK | |
54 | + properties.setProperty(KAPTCHA_BORDER_COLOR, "105,179,90"); | |
55 | + // 验证码文本字符颜色 默认为Color.BLACK | |
56 | + properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_COLOR, "blue"); | |
57 | + // 验证码图片宽度 默认为200 | |
58 | + properties.setProperty(KAPTCHA_IMAGE_WIDTH, "160"); | |
59 | + // 验证码图片高度 默认为50 | |
60 | + properties.setProperty(KAPTCHA_IMAGE_HEIGHT, "60"); | |
61 | + // 验证码文本字符大小 默认为40 | |
62 | + properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_SIZE, "35"); | |
63 | + // KAPTCHA_SESSION_KEY | |
64 | + properties.setProperty(KAPTCHA_SESSION_CONFIG_KEY, "kaptchaCodeMath"); | |
65 | + // 验证码文本生成器 | |
66 | + properties.setProperty(KAPTCHA_TEXTPRODUCER_IMPL, "com.ruoyi.framework.config.KaptchaTextCreator"); | |
67 | + // 验证码文本字符间距 默认为2 | |
68 | + properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_SPACE, "3"); | |
69 | + // 验证码文本字符长度 默认为5 | |
70 | + properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_LENGTH, "6"); | |
71 | + // 验证码文本字体样式 默认为new Font("Arial", 1, fontSize), new Font("Courier", 1, fontSize) | |
72 | + properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_NAMES, "Arial,Courier"); | |
73 | + // 验证码噪点颜色 默认为Color.BLACK | |
74 | + properties.setProperty(KAPTCHA_NOISE_COLOR, "white"); | |
75 | + // 干扰实现类 | |
76 | + properties.setProperty(KAPTCHA_NOISE_IMPL, "com.google.code.kaptcha.impl.NoNoise"); | |
77 | + // 图片样式 水纹com.google.code.kaptcha.impl.WaterRipple 鱼眼com.google.code.kaptcha.impl.FishEyeGimpy 阴影com.google.code.kaptcha.impl.ShadowGimpy | |
78 | + properties.setProperty(KAPTCHA_OBSCURIFICATOR_IMPL, "com.google.code.kaptcha.impl.ShadowGimpy"); | |
79 | + Config config = new Config(properties); | |
80 | + defaultKaptcha.setConfig(config); | |
81 | + return defaultKaptcha; | |
82 | + } | |
83 | +} | |
... | ... |
ruoyi-framework/src/main/java/com/ruoyi/framework/config/KaptchaTextCreator.java
0 → 100644
1 | +package com.ruoyi.framework.config; | |
2 | + | |
3 | +import java.util.Random; | |
4 | +import com.google.code.kaptcha.text.impl.DefaultTextCreator; | |
5 | + | |
6 | +/** | |
7 | + * 验证码文本生成器 | |
8 | + * | |
9 | + * @author ruoyi | |
10 | + */ | |
11 | +public class KaptchaTextCreator extends DefaultTextCreator | |
12 | +{ | |
13 | + private static final String[] CNUMBERS = "0,1,2,3,4,5,6,7,8,9,10".split(","); | |
14 | + | |
15 | + @Override | |
16 | + public String getText() | |
17 | + { | |
18 | + Integer result = 0; | |
19 | + Random random = new Random(); | |
20 | + int x = random.nextInt(10); | |
21 | + int y = random.nextInt(10); | |
22 | + StringBuilder suChinese = new StringBuilder(); | |
23 | + int randomoperands = (int) Math.round(Math.random() * 2); | |
24 | + if (randomoperands == 0) | |
25 | + { | |
26 | + result = x * y; | |
27 | + suChinese.append(CNUMBERS[x]); | |
28 | + suChinese.append("*"); | |
29 | + suChinese.append(CNUMBERS[y]); | |
30 | + } | |
31 | + else if (randomoperands == 1) | |
32 | + { | |
33 | + if (!(x == 0) && y % x == 0) | |
34 | + { | |
35 | + result = y / x; | |
36 | + suChinese.append(CNUMBERS[y]); | |
37 | + suChinese.append("/"); | |
38 | + suChinese.append(CNUMBERS[x]); | |
39 | + } | |
40 | + else | |
41 | + { | |
42 | + result = x + y; | |
43 | + suChinese.append(CNUMBERS[x]); | |
44 | + suChinese.append("+"); | |
45 | + suChinese.append(CNUMBERS[y]); | |
46 | + } | |
47 | + } | |
48 | + else if (randomoperands == 2) | |
49 | + { | |
50 | + if (x >= y) | |
51 | + { | |
52 | + result = x - y; | |
53 | + suChinese.append(CNUMBERS[x]); | |
54 | + suChinese.append("-"); | |
55 | + suChinese.append(CNUMBERS[y]); | |
56 | + } | |
57 | + else | |
58 | + { | |
59 | + result = y - x; | |
60 | + suChinese.append(CNUMBERS[y]); | |
61 | + suChinese.append("-"); | |
62 | + suChinese.append(CNUMBERS[x]); | |
63 | + } | |
64 | + } | |
65 | + else | |
66 | + { | |
67 | + result = x + y; | |
68 | + suChinese.append(CNUMBERS[x]); | |
69 | + suChinese.append("+"); | |
70 | + suChinese.append(CNUMBERS[y]); | |
71 | + } | |
72 | + suChinese.append("=?@" + result); | |
73 | + return suChinese.toString(); | |
74 | + } | |
75 | +} | |
0 | 76 | \ No newline at end of file |
... | ... |
ruoyi-ui/src/views/login.vue
... | ... | @@ -29,7 +29,7 @@ |
29 | 29 | <svg-icon slot="prefix" icon-class="validCode" class="el-input__icon input-icon" /> |
30 | 30 | </el-input> |
31 | 31 | <div class="login-code"> |
32 | - <img :src="codeUrl" @click="getCode" /> | |
32 | + <img :src="codeUrl" @click="getCode" class="login-code-img"/> | |
33 | 33 | </div> |
34 | 34 | </el-form-item> |
35 | 35 | <el-checkbox v-model="loginForm.rememberMe" style="margin:0px 0px 25px 0px;">记住密码</el-checkbox> |
... | ... | @@ -200,4 +200,7 @@ export default { |
200 | 200 | font-size: 12px; |
201 | 201 | letter-spacing: 1px; |
202 | 202 | } |
203 | +.login-code-img { | |
204 | + height: 38px; | |
205 | +} | |
203 | 206 | </style> |
... | ... |