| package com.doumee.service.common; | 
|   | 
| import com.doumee.core.exception.BusinessException; | 
| import com.doumee.core.constants.ResponseStatus; | 
| import com.doumee.service.proxy.CacheProxy; | 
| import com.fasterxml.jackson.annotation.JsonIgnore; | 
| import io.swagger.annotations.ApiModel; | 
| import io.swagger.annotations.ApiModelProperty; | 
| import lombok.Data; | 
| import org.springframework.beans.factory.annotation.Autowired; | 
| import org.springframework.beans.factory.annotation.Value; | 
| import org.springframework.stereotype.Service; | 
|   | 
| import javax.imageio.ImageIO; | 
| import java.awt.*; | 
| import java.awt.image.BufferedImage; | 
| import java.io.ByteArrayOutputStream; | 
| import java.io.IOException; | 
| import java.util.Base64; | 
| import java.util.Random; | 
| import java.util.UUID; | 
|   | 
| /** | 
|  * 图片验证码 | 
|  * @author Eva.Caesar Liu | 
|  * @date 2023/03/21 14:49 | 
|  */ | 
| @Service | 
| public class CaptchaService { | 
|   | 
|     @Value("${cache.captcha.expire}") | 
|     private int expire; | 
|   | 
|     // 验证码字符集 | 
|     private static final char[] CODES = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', | 
|             'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', | 
|             'X', 'Y', 'a', 'b', 'c', 'd', 'e', 'f', 'h', 'j', | 
|             'k', 'l', 'm', 'n', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', | 
|             'x', 'y', '3', '4', '5', '6', '7', '8' }; | 
|   | 
|     // 验证码图片宽度 | 
|     private static final Integer WIDTH = 90; | 
|   | 
|     // 验证码图片高度 | 
|     private static final Integer HEIGHT = 30; | 
|   | 
|     @Autowired | 
|     private CacheProxy<String, String> cacheProxy; | 
|   | 
|     /** | 
|      * 生成验证码图片 | 
|      * @author Eva.Caesar Liu | 
|      * @date 2023/03/21 14:49 | 
|      */ | 
|     public Captcha genCaptcha () { | 
|         // 定义图像buffer | 
|         BufferedImage buffImg = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB); | 
|         Graphics gd = buffImg.getGraphics(); | 
|         // 创建一个随机数生成器类 | 
|         Random random = new Random(); | 
|         // 将图像填充为白色 | 
|         gd.setColor(Color.decode("#FFFFFF")); | 
|         gd.fillRect(0, 0, WIDTH, HEIGHT); | 
|         // 创建字体,字体的大小应该根据图片的高度来定。 | 
|         Font font = new Font("Dialog", 1, 23); | 
|         // 设置字体 | 
|         gd.setFont(font); | 
|         // 画边框 | 
|         // gd.setColor(Color.decode("#E6BA09")); | 
|         gd.drawRect(0, 0, WIDTH, HEIGHT); | 
|         // 随机产生40条干扰线,使图象中的认证码不易被其它程序探测到。 | 
|         gd.setColor(Color.decode("#990000")); | 
|         for (int i = 0; i < 30; i++) { | 
|             int x = random.nextInt(WIDTH); | 
|             int y = random.nextInt(HEIGHT); | 
|             int xl = random.nextInt(12); | 
|             int yl = random.nextInt(12); | 
|             gd.drawLine(x, y, x + xl, y + yl); | 
|         } | 
|         // randomCode用于保存随机产生的验证码,以便用户登录后进行验证。 | 
|         StringBuffer randomCode = new StringBuffer(); | 
|         int red, green, blue; | 
|         // 随机产生codeCount数字的验证码。 | 
|         for (int i = 0; i < 4; i++) { | 
|             // 得到随机产生的验证码数字。 | 
|             String code = String.valueOf(CODES[random.nextInt(CODES.length)]); | 
|             // 产生随机的颜色分量来构造颜色值,这样输出的每位数字的颜色值都将不同。 | 
|             red = random.nextInt(255); | 
|             green = random.nextInt(255); | 
|             blue = random.nextInt(255); | 
|   | 
|             // 用随机产生的颜色将验证码绘制到图像中。 | 
|             gd.setColor(new Color(red, green, blue)); | 
|             gd.drawString(code, (i + 1) * 15, 24); | 
|   | 
|             // 将产生的四个随机数组合在一起。 | 
|             randomCode.append(code); | 
|         } | 
|         // 存入缓存 | 
|         Captcha captcha = new Captcha(randomCode.toString(), buffImg); | 
|         cacheProxy.put(captcha.getUuid(), captcha.getText(), expire); | 
|         return captcha; | 
|     } | 
|   | 
|     /** | 
|      * 校验验证码 | 
|      * @author Eva.Caesar Liu | 
|      * @date 2023/03/21 14:49 | 
|      */ | 
|     public void check(String uuid, String code) { | 
|         String cacheCode = cacheProxy.get(uuid); | 
|         if (cacheCode == null) { | 
|             throw new BusinessException(ResponseStatus.VERIFICATION_CODE_INCORRECT); | 
|         } | 
|         cacheProxy.remove(cacheCode); | 
|         if (!code.equalsIgnoreCase(cacheCode)) { | 
|             throw new BusinessException(ResponseStatus.VERIFICATION_CODE_INCORRECT); | 
|         } | 
|     } | 
|   | 
|     /** | 
|      * @author Eva.Caesar Liu | 
|      * @date 2023/03/21 14:49 | 
|      */ | 
|     @Data | 
|     @ApiModel("验证码对象") | 
|     public static class Captcha { | 
|   | 
|         @ApiModelProperty(value = "验证码UUID") | 
|         private String uuid; | 
|   | 
|         @JsonIgnore | 
|         @ApiModelProperty(value = "验证码", hidden = true) | 
|         private String text; | 
|   | 
|         @ApiModelProperty(value = "验证码Base64") | 
|         private String image; | 
|   | 
|         Captcha (String text, BufferedImage image) { | 
|             this.uuid = UUID.randomUUID().toString(); | 
|             this.text = text; | 
|             ByteArrayOutputStream stream = new ByteArrayOutputStream(); | 
|             try { | 
|                 ImageIO.write(image, "png", stream); | 
|             } catch (IOException e) { | 
|                 throw new RuntimeException("生成验证码失败"); | 
|             } | 
|             this.image = "data:image/png;base64," + Base64.getEncoder().encodeToString(stream.toByteArray()); | 
|         } | 
|     } | 
| } |