正在显示
7 个修改的文件
包含
228 行增加
和
21 行删除
| @@ -21,6 +21,7 @@ | @@ -21,6 +21,7 @@ | ||
| 21 | <druid.version>1.1.14</druid.version> | 21 | <druid.version>1.1.14</druid.version> |
| 22 | <bitwalker.version>1.19</bitwalker.version> | 22 | <bitwalker.version>1.19</bitwalker.version> |
| 23 | <swagger.version>2.9.2</swagger.version> | 23 | <swagger.version>2.9.2</swagger.version> |
| 24 | + <kaptcha.version>2.3.2</kaptcha.version> | ||
| 24 | <pagehelper.boot.version>1.2.5</pagehelper.boot.version> | 25 | <pagehelper.boot.version>1.2.5</pagehelper.boot.version> |
| 25 | <fastjson.version>1.2.70</fastjson.version> | 26 | <fastjson.version>1.2.70</fastjson.version> |
| 26 | <oshi.version>3.9.1</oshi.version> | 27 | <oshi.version>3.9.1</oshi.version> |
| @@ -137,6 +138,13 @@ | @@ -137,6 +138,13 @@ | ||
| 137 | <artifactId>jjwt</artifactId> | 138 | <artifactId>jjwt</artifactId> |
| 138 | <version>${jwt.version}</version> | 139 | <version>${jwt.version}</version> |
| 139 | </dependency> | 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 | <dependency> | 150 | <dependency> |
| 1 | package com.ruoyi.web.controller.common; | 1 | package com.ruoyi.web.controller.common; |
| 2 | 2 | ||
| 3 | -import java.io.ByteArrayOutputStream; | 3 | +import java.awt.image.BufferedImage; |
| 4 | import java.io.IOException; | 4 | import java.io.IOException; |
| 5 | import java.util.concurrent.TimeUnit; | 5 | import java.util.concurrent.TimeUnit; |
| 6 | +import javax.annotation.Resource; | ||
| 7 | +import javax.imageio.ImageIO; | ||
| 6 | import javax.servlet.http.HttpServletResponse; | 8 | import javax.servlet.http.HttpServletResponse; |
| 7 | import org.springframework.beans.factory.annotation.Autowired; | 9 | import org.springframework.beans.factory.annotation.Autowired; |
| 10 | +import org.springframework.beans.factory.annotation.Value; | ||
| 11 | +import org.springframework.util.FastByteArrayOutputStream; | ||
| 8 | import org.springframework.web.bind.annotation.GetMapping; | 12 | import org.springframework.web.bind.annotation.GetMapping; |
| 9 | import org.springframework.web.bind.annotation.RestController; | 13 | import org.springframework.web.bind.annotation.RestController; |
| 14 | +import com.google.code.kaptcha.Producer; | ||
| 10 | import com.ruoyi.common.constant.Constants; | 15 | import com.ruoyi.common.constant.Constants; |
| 11 | import com.ruoyi.common.core.domain.AjaxResult; | 16 | import com.ruoyi.common.core.domain.AjaxResult; |
| 12 | import com.ruoyi.common.core.redis.RedisCache; | 17 | import com.ruoyi.common.core.redis.RedisCache; |
| 13 | -import com.ruoyi.common.utils.VerifyCodeUtils; | ||
| 14 | import com.ruoyi.common.utils.sign.Base64; | 18 | import com.ruoyi.common.utils.sign.Base64; |
| 15 | import com.ruoyi.common.utils.uuid.IdUtils; | 19 | import com.ruoyi.common.utils.uuid.IdUtils; |
| 16 | 20 | ||
| @@ -22,8 +26,18 @@ import com.ruoyi.common.utils.uuid.IdUtils; | @@ -22,8 +26,18 @@ import com.ruoyi.common.utils.uuid.IdUtils; | ||
| 22 | @RestController | 26 | @RestController |
| 23 | public class CaptchaController | 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 | @Autowired | 35 | @Autowired |
| 26 | private RedisCache redisCache; | 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,32 +45,42 @@ public class CaptchaController | ||
| 31 | @GetMapping("/captchaImage") | 45 | @GetMapping("/captchaImage") |
| 32 | public AjaxResult getCode(HttpServletResponse response) throws IOException | 46 | public AjaxResult getCode(HttpServletResponse response) throws IOException |
| 33 | { | 47 | { |
| 34 | - // 生成随机字串 | ||
| 35 | - String verifyCode = VerifyCodeUtils.generateVerifyCode(4); | ||
| 36 | - // 唯一标识 | 48 | + // 保存验证码信息 |
| 37 | String uuid = IdUtils.simpleUUID(); | 49 | String uuid = IdUtils.simpleUUID(); |
| 38 | String verifyKey = Constants.CAPTCHA_CODE_KEY + uuid; | 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 | } |
| @@ -35,6 +35,18 @@ | @@ -35,6 +35,18 @@ | ||
| 35 | <artifactId>druid-spring-boot-starter</artifactId> | 35 | <artifactId>druid-spring-boot-starter</artifactId> |
| 36 | </dependency> | 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 | <dependency> | 51 | <dependency> |
| 40 | <groupId>com.github.oshi</groupId> | 52 | <groupId>com.github.oshi</groupId> |
| 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 | +} |
| 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 | +} |
| @@ -29,7 +29,7 @@ | @@ -29,7 +29,7 @@ | ||
| 29 | <svg-icon slot="prefix" icon-class="validCode" class="el-input__icon input-icon" /> | 29 | <svg-icon slot="prefix" icon-class="validCode" class="el-input__icon input-icon" /> |
| 30 | </el-input> | 30 | </el-input> |
| 31 | <div class="login-code"> | 31 | <div class="login-code"> |
| 32 | - <img :src="codeUrl" @click="getCode" /> | 32 | + <img :src="codeUrl" @click="getCode" class="login-code-img"/> |
| 33 | </div> | 33 | </div> |
| 34 | </el-form-item> | 34 | </el-form-item> |
| 35 | <el-checkbox v-model="loginForm.rememberMe" style="margin:0px 0px 25px 0px;">记住密码</el-checkbox> | 35 | <el-checkbox v-model="loginForm.rememberMe" style="margin:0px 0px 25px 0px;">记住密码</el-checkbox> |
| @@ -200,4 +200,7 @@ export default { | @@ -200,4 +200,7 @@ export default { | ||
| 200 | font-size: 12px; | 200 | font-size: 12px; |
| 201 | letter-spacing: 1px; | 201 | letter-spacing: 1px; |
| 202 | } | 202 | } |
| 203 | +.login-code-img { | ||
| 204 | + height: 38px; | ||
| 205 | +} | ||
| 203 | </style> | 206 | </style> |
-
请 注册 或 登录 后发表评论