From 6ebe8da9b42e773f48260c6d1e0b90307bde5303 Mon Sep 17 00:00:00 2001
From: kongzy <kongzy>
Date: 星期一, 27 十一月 2023 09:28:00 +0800
Subject: [PATCH] 增加验证码

---
 assess-system/src/main/java/com/gkhy/assess/system/service/CaptchaService.java           |   15 +
 assess-common/src/main/java/com/gkhy/assess/common/config/KaptchaTextCreator.java        |   68 ++++++++
 assess-framework/src/main/java/com/gkhy/assess/framework/config/DruidConfig.java         |    2 
 assess-system/src/main/java/com/gkhy/assess/system/service/impl/SysUserServiceImpl.java  |   29 +++
 assess-common/src/main/java/com/gkhy/assess/common/domain/vo/LoginBody.java              |    2 
 assess-admin/src/main/java/com/gkhy/assess/admin/controller/CaptchaController.java       |   33 ++++
 assess-common/pom.xml                                                                    |    4 
 assess-framework/src/main/java/com/gkhy/assess/framework/shiro/ShiroConfig.java          |    1 
 pom.xml                                                                                  |    8 +
 assess-common/src/main/java/com/gkhy/assess/common/constant/CacheConstant.java           |    2 
 assess-common/src/main/java/com/gkhy/assess/common/config/CaptchaConfig.java             |   84 ++++++++++
 assess-framework/src/main/java/com/gkhy/assess/framework/interceptor/LogInterceptor.java |    2 
 assess-system/src/main/java/com/gkhy/assess/system/utils/ShiroUtils.java                 |    2 
 assess-system/src/main/java/com/gkhy/assess/system/service/impl/CaptchaServiceImpl.java  |   60 +++++++
 assess-system/src/main/java/com/gkhy/assess/system/domain/vo/CaptchaVO.java              |   14 +
 assess-framework/src/main/java/com/gkhy/assess/framework/aop/LogAspect.java              |  130 ++++++++++++++++
 16 files changed, 453 insertions(+), 3 deletions(-)

diff --git a/assess-admin/src/main/java/com/gkhy/assess/admin/controller/CaptchaController.java b/assess-admin/src/main/java/com/gkhy/assess/admin/controller/CaptchaController.java
new file mode 100644
index 0000000..18b4e2e
--- /dev/null
+++ b/assess-admin/src/main/java/com/gkhy/assess/admin/controller/CaptchaController.java
@@ -0,0 +1,33 @@
+package com.gkhy.assess.admin.controller;
+
+import com.gkhy.assess.common.api.CommonResult;
+import com.gkhy.assess.system.service.CaptchaService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * 验证码操作处理
+ * 
+ * @author ruoyi
+ */
+@Api(tags = "验证码前端控制器")
+@RestController
+@RequestMapping("/system/captcha")
+public class CaptchaController
+{
+    @Autowired
+    private CaptchaService captchaService;
+    /**
+     * 生成验证码
+     */
+    @ApiOperation(value = "生成验证码")
+    @GetMapping("/captchaImage")
+    public CommonResult captchaImage()
+    {
+       return CommonResult.success(captchaService.captchaImage());
+    }
+}
diff --git a/assess-common/pom.xml b/assess-common/pom.xml
index 1da62fb..78cd94b 100644
--- a/assess-common/pom.xml
+++ b/assess-common/pom.xml
@@ -107,6 +107,10 @@
             <groupId>com.github.ben-manes.caffeine</groupId>
             <artifactId>caffeine</artifactId>
         </dependency>
+        <dependency>
+            <groupId>com.github.penggle</groupId>
+            <artifactId>kaptcha</artifactId>
+        </dependency>
     </dependencies>
 
 </project>
\ No newline at end of file
diff --git a/assess-common/src/main/java/com/gkhy/assess/common/config/CaptchaConfig.java b/assess-common/src/main/java/com/gkhy/assess/common/config/CaptchaConfig.java
new file mode 100644
index 0000000..35d2fe4
--- /dev/null
+++ b/assess-common/src/main/java/com/gkhy/assess/common/config/CaptchaConfig.java
@@ -0,0 +1,84 @@
+package com.gkhy.assess.common.config;
+
+import com.google.code.kaptcha.impl.DefaultKaptcha;
+import com.google.code.kaptcha.util.Config;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import java.util.Properties;
+
+import static com.google.code.kaptcha.Constants.*;
+
+/**
+ * 验证码配置
+ *
+ */
+@Configuration
+public class CaptchaConfig
+{
+    @Bean(name = "captchaProducer")
+    public DefaultKaptcha getKaptchaBean()
+    {
+        DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
+        Properties properties = new Properties();
+        // 是否有边框 默认为true 我们可以自己设置yes,no
+        properties.setProperty(KAPTCHA_BORDER, "yes");
+        // 验证码文本字符颜色 默认为Color.BLACK
+        properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_COLOR, "black");
+        // 验证码图片宽度 默认为200
+        properties.setProperty(KAPTCHA_IMAGE_WIDTH, "160");
+        // 验证码图片高度 默认为50
+        properties.setProperty(KAPTCHA_IMAGE_HEIGHT, "60");
+        // 验证码文本字符大小 默认为40
+        properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_SIZE, "38");
+        // KAPTCHA_SESSION_KEY
+        properties.setProperty(KAPTCHA_SESSION_CONFIG_KEY, "kaptchaCode");
+        // 验证码文本字符长度 默认为5
+        properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_LENGTH, "4");
+        // 验证码文本字体样式 默认为new Font("Arial", 1, fontSize), new Font("Courier", 1, fontSize)
+        properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_NAMES, "Arial,Courier");
+        // 图片样式 水纹com.google.code.kaptcha.impl.WaterRipple 鱼眼com.google.code.kaptcha.impl.FishEyeGimpy 阴影com.google.code.kaptcha.impl.ShadowGimpy
+        properties.setProperty(KAPTCHA_OBSCURIFICATOR_IMPL, "com.google.code.kaptcha.impl.ShadowGimpy");
+        Config config = new Config(properties);
+        defaultKaptcha.setConfig(config);
+        return defaultKaptcha;
+    }
+
+    @Bean(name = "captchaProducerMath")
+    public DefaultKaptcha getKaptchaBeanMath()
+    {
+        DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
+        Properties properties = new Properties();
+        // 是否有边框 默认为true 我们可以自己设置yes,no
+        properties.setProperty(KAPTCHA_BORDER, "yes");
+        // 边框颜色 默认为Color.BLACK
+        properties.setProperty(KAPTCHA_BORDER_COLOR, "105,179,90");
+        // 验证码文本字符颜色 默认为Color.BLACK
+        properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_COLOR, "blue");
+        // 验证码图片宽度 默认为200
+        properties.setProperty(KAPTCHA_IMAGE_WIDTH, "160");
+        // 验证码图片高度 默认为50
+        properties.setProperty(KAPTCHA_IMAGE_HEIGHT, "60");
+        // 验证码文本字符大小 默认为40
+        properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_SIZE, "35");
+        // KAPTCHA_SESSION_KEY
+        properties.setProperty(KAPTCHA_SESSION_CONFIG_KEY, "kaptchaCodeMath");
+        // 验证码文本生成器
+        properties.setProperty(KAPTCHA_TEXTPRODUCER_IMPL, "com.gkhy.assess.common.config.KaptchaTextCreator");
+        // 验证码文本字符间距 默认为2
+        properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_SPACE, "3");
+        // 验证码文本字符长度 默认为5
+        properties.setProperty(KAPTCHA_TEXTPRODUCER_CHAR_LENGTH, "6");
+        // 验证码文本字体样式 默认为new Font("Arial", 1, fontSize), new Font("Courier", 1, fontSize)
+        properties.setProperty(KAPTCHA_TEXTPRODUCER_FONT_NAMES, "Arial,Courier");
+        // 验证码噪点颜色 默认为Color.BLACK
+        properties.setProperty(KAPTCHA_NOISE_COLOR, "white");
+        // 干扰实现类
+        properties.setProperty(KAPTCHA_NOISE_IMPL, "com.google.code.kaptcha.impl.NoNoise");
+        // 图片样式 水纹com.google.code.kaptcha.impl.WaterRipple 鱼眼com.google.code.kaptcha.impl.FishEyeGimpy 阴影com.google.code.kaptcha.impl.ShadowGimpy
+        properties.setProperty(KAPTCHA_OBSCURIFICATOR_IMPL, "com.google.code.kaptcha.impl.ShadowGimpy");
+        Config config = new Config(properties);
+        defaultKaptcha.setConfig(config);
+        return defaultKaptcha;
+    }
+}
diff --git a/assess-common/src/main/java/com/gkhy/assess/common/config/KaptchaTextCreator.java b/assess-common/src/main/java/com/gkhy/assess/common/config/KaptchaTextCreator.java
new file mode 100644
index 0000000..0678e70
--- /dev/null
+++ b/assess-common/src/main/java/com/gkhy/assess/common/config/KaptchaTextCreator.java
@@ -0,0 +1,68 @@
+package com.gkhy.assess.common.config;
+
+import com.google.code.kaptcha.text.impl.DefaultTextCreator;
+
+import java.util.Random;
+
+/**
+ * 验证码文本生成器
+ *
+ */
+public class KaptchaTextCreator extends DefaultTextCreator
+{
+    private static final String[] CNUMBERS = "0,1,2,3,4,5,6,7,8,9,10".split(",");
+
+    @Override
+    public String getText()
+    {
+        Integer result = 0;
+        Random random = new Random();
+        int x = random.nextInt(10);
+        int y = random.nextInt(10);
+        StringBuilder suChinese = new StringBuilder();
+        int randomoperands = random.nextInt(3);
+        if (randomoperands == 0)
+        {
+            result = x * y;
+            suChinese.append(CNUMBERS[x]);
+            suChinese.append("*");
+            suChinese.append(CNUMBERS[y]);
+        }
+        else if (randomoperands == 1)
+        {
+            if ((x != 0) && y % x == 0)
+            {
+                result = y / x;
+                suChinese.append(CNUMBERS[y]);
+                suChinese.append("/");
+                suChinese.append(CNUMBERS[x]);
+            }
+            else
+            {
+                result = x + y;
+                suChinese.append(CNUMBERS[x]);
+                suChinese.append("+");
+                suChinese.append(CNUMBERS[y]);
+            }
+        }
+        else
+        {
+            if (x >= y)
+            {
+                result = x - y;
+                suChinese.append(CNUMBERS[x]);
+                suChinese.append("-");
+                suChinese.append(CNUMBERS[y]);
+            }
+            else
+            {
+                result = y - x;
+                suChinese.append(CNUMBERS[y]);
+                suChinese.append("-");
+                suChinese.append(CNUMBERS[x]);
+            }
+        }
+        suChinese.append("=?@" + result);
+        return suChinese.toString();
+    }
+}
\ No newline at end of file
diff --git a/assess-common/src/main/java/com/gkhy/assess/common/constant/CacheConstant.java b/assess-common/src/main/java/com/gkhy/assess/common/constant/CacheConstant.java
index e619920..053462c 100644
--- a/assess-common/src/main/java/com/gkhy/assess/common/constant/CacheConstant.java
+++ b/assess-common/src/main/java/com/gkhy/assess/common/constant/CacheConstant.java
@@ -11,6 +11,8 @@
 
     String SYS_CONFIG_KEY = "sys_config:";
 
+    String CAPTCHA_CODE_KEY = "captcha_codes:";
+
 
 
 
diff --git a/assess-common/src/main/java/com/gkhy/assess/common/domain/vo/LoginBody.java b/assess-common/src/main/java/com/gkhy/assess/common/domain/vo/LoginBody.java
index 6765e9a..2910ef7 100644
--- a/assess-common/src/main/java/com/gkhy/assess/common/domain/vo/LoginBody.java
+++ b/assess-common/src/main/java/com/gkhy/assess/common/domain/vo/LoginBody.java
@@ -19,6 +19,6 @@
     private String password;
     @ApiModelProperty(value = "验证码",required = false)
     private String code;
-    @ApiModelProperty(value = "唯一标识",required = false)
+    @ApiModelProperty(value = "验证码唯一标识",required = false)
     private String uuid;
 }
diff --git a/assess-framework/src/main/java/com/gkhy/assess/framework/aop/LogAspect.java b/assess-framework/src/main/java/com/gkhy/assess/framework/aop/LogAspect.java
new file mode 100644
index 0000000..41fed63
--- /dev/null
+++ b/assess-framework/src/main/java/com/gkhy/assess/framework/aop/LogAspect.java
@@ -0,0 +1,130 @@
+package com.gkhy.assess.framework.aop;
+
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.core.util.URLUtil;
+import cn.hutool.extra.servlet.ServletUtil;
+import cn.hutool.json.JSONObject;
+import com.gkhy.assess.system.utils.ShiroUtils;
+import lombok.extern.slf4j.Slf4j;
+import org.aspectj.lang.JoinPoint;
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.Signature;
+import org.aspectj.lang.annotation.AfterThrowing;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Pointcut;
+import org.aspectj.lang.reflect.MethodSignature;
+import org.springframework.stereotype.Component;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+
+import javax.servlet.http.HttpServletRequest;
+import java.lang.reflect.Method;
+import java.lang.reflect.Parameter;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@Aspect
+@Component
+@Slf4j
+public class LogAspect {
+    @Pointcut("execution(public * com.gkhy.assess.*.controller..*.*(..))")
+    public void logPointCut(){
+
+    }
+
+    /**
+     * 处理完请求后执行
+     *
+     * @param joinPoint 切点
+     */
+    @Around("logPointCut()")
+    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable{
+        long startTime = System.currentTimeMillis();
+        //获取当前请求对象
+        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
+        HttpServletRequest request = attributes.getRequest();
+        Signature signature = joinPoint.getSignature();
+        MethodSignature methodSignature = (MethodSignature) signature;
+        Method method = methodSignature.getMethod();
+        StringBuffer requestURL = request.getRequestURL();
+        JSONObject webLog = new JSONObject();
+        String urlStr = request.getRequestURL().toString();
+        webLog.put("basePath",StrUtil.removeSuffix(urlStr, URLUtil.url(urlStr).getPath()));
+        webLog.put("ip", ServletUtil.getClientIP(request,null));
+        webLog.put("method",request.getMethod());
+        Object params=getParameter(method, joinPoint.getArgs());
+        webLog.put("parameter",params);
+        webLog.put("uri",request.getRequestURI());
+        webLog.put("url",requestURL.toString());
+        String userId=ShiroUtils.getUserId()!=null?String.valueOf(ShiroUtils.getUserId()):"";
+        webLog.put("userId",userId);
+        log.info(webLog.toString());
+        Object result = joinPoint.proceed();
+        if (result == null) {
+            //如果切到了 没有返回类型的void方法,这里直接返回
+            return null;
+        }
+        long endTime = System.currentTimeMillis();
+        webLog.put("result",result);
+        webLog.put("spendTime",endTime - startTime);
+        log.info(webLog.toString());
+        return result;
+    }
+
+    /**
+     * 拦截异常操作
+     *
+     * @param joinPoint 切点
+     * @param e 异常
+     */
+    @AfterThrowing(value = "logPointCut()", throwing = "e")
+    public void doAfterThrowing(JoinPoint joinPoint, Exception e)
+    {
+        //获取当前请求对象
+        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
+        HttpServletRequest request = attributes.getRequest();
+        String urlStr = request.getRequestURL().toString();
+        log.error("@AfterThrowing异常通知:url={},出错了error_message={}", urlStr,e.getMessage());
+    }
+
+
+
+    /**
+     * 根据方法和传入的参数获取请求参数
+     */
+    private Object getParameter(Method method, Object[] args) {
+        List<Object> argList = new ArrayList<>();
+        Parameter[] parameters = method.getParameters();
+        for (int i = 0; i < parameters.length; i++) {
+            //将RequestBody注解修饰的参数作为请求参数
+            RequestBody requestBody = parameters[i].getAnnotation(RequestBody.class);
+            if (requestBody != null) {
+                argList.add(args[i]);
+            }
+            //将RequestParam注解修饰的参数作为请求参数
+            RequestParam requestParam = parameters[i].getAnnotation(RequestParam.class);
+            if (requestParam != null) {
+                Map<String, Object> map = new HashMap<>();
+                String key = parameters[i].getName();
+                if (StrUtil.isNotEmpty(requestParam.value())) {
+                    key = requestParam.value();
+                }
+                map.put(key, args[i]);
+                argList.add(map);
+            }
+        }
+        if (argList.size() == 0) {
+            return null;
+        } else if (argList.size() == 1) {
+            return argList.get(0);
+        } else {
+            return argList;
+        }
+    }
+
+}
diff --git a/assess-framework/src/main/java/com/gkhy/assess/framework/config/DruidConfig.java b/assess-framework/src/main/java/com/gkhy/assess/framework/config/DruidConfig.java
index c3aa3c5..ff4fa0a 100644
--- a/assess-framework/src/main/java/com/gkhy/assess/framework/config/DruidConfig.java
+++ b/assess-framework/src/main/java/com/gkhy/assess/framework/config/DruidConfig.java
@@ -80,6 +80,7 @@
         String commonJsPattern=pattern.replaceAll("\\*","js/common.js");
         final String filePath="support/http/resources/js/common.js";
         Filter filter=new Filter() {
+
             @Override
             public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
                 filterChain.doFilter(servletRequest,servletResponse);
@@ -91,6 +92,7 @@
                 text=text.replaceAll("powered.*?shrek.wang</a>","");
                 servletResponse.getWriter().write(text);
             }
+
         };
 
 
diff --git a/assess-framework/src/main/java/com/gkhy/assess/framework/interceptor/LogInterceptor.java b/assess-framework/src/main/java/com/gkhy/assess/framework/interceptor/LogInterceptor.java
index db4a372..03740b0 100644
--- a/assess-framework/src/main/java/com/gkhy/assess/framework/interceptor/LogInterceptor.java
+++ b/assess-framework/src/main/java/com/gkhy/assess/framework/interceptor/LogInterceptor.java
@@ -26,7 +26,7 @@
         MDC.put("requestId", requestId);
         MDC.put("clientIP", ip);
         MDC.put("url", url);
-        String userId= String.valueOf(ShiroUtils.getUserId());
+        String userId= ShiroUtils.getUserId()!=null?String.valueOf(ShiroUtils.getUserId()):"";
         MDC.put("userId", userId);
         return true;
     }
diff --git a/assess-framework/src/main/java/com/gkhy/assess/framework/shiro/ShiroConfig.java b/assess-framework/src/main/java/com/gkhy/assess/framework/shiro/ShiroConfig.java
index a594680..1e30371 100644
--- a/assess-framework/src/main/java/com/gkhy/assess/framework/shiro/ShiroConfig.java
+++ b/assess-framework/src/main/java/com/gkhy/assess/framework/shiro/ShiroConfig.java
@@ -75,6 +75,7 @@
         chain.addPathDefinition("/system/agency/getAgencyById","anon");
 
         chain.addPathDefinition("/system/user/agencyRegister","anon");
+        chain.addPathDefinition("/system/captcha/captchaImage","anon");
 
         //除了以上的请求外,其它请求都需要登录
         chain.addPathDefinition("/**", "jwtFilter,authc"); // 需登录才能访问
diff --git a/assess-system/src/main/java/com/gkhy/assess/system/domain/vo/CaptchaVO.java b/assess-system/src/main/java/com/gkhy/assess/system/domain/vo/CaptchaVO.java
new file mode 100644
index 0000000..c9db5d6
--- /dev/null
+++ b/assess-system/src/main/java/com/gkhy/assess/system/domain/vo/CaptchaVO.java
@@ -0,0 +1,14 @@
+package com.gkhy.assess.system.domain.vo;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+@Data
+@ApiModel(value = "验证码对象", description = "验证码对象")
+public class CaptchaVO {
+    @ApiModelProperty("唯一标识")
+    private String uuid;
+    @ApiModelProperty("验证码图片内容,base64")
+    private String image;
+}
diff --git a/assess-system/src/main/java/com/gkhy/assess/system/service/CaptchaService.java b/assess-system/src/main/java/com/gkhy/assess/system/service/CaptchaService.java
new file mode 100644
index 0000000..9361b00
--- /dev/null
+++ b/assess-system/src/main/java/com/gkhy/assess/system/service/CaptchaService.java
@@ -0,0 +1,15 @@
+package com.gkhy.assess.system.service;
+
+import com.gkhy.assess.system.domain.vo.CaptchaVO;
+
+import java.awt.image.BufferedImage;
+
+public interface CaptchaService {
+
+    /**
+     * 生成验证码图片
+     * @return
+     */
+    CaptchaVO captchaImage();
+
+}
diff --git a/assess-system/src/main/java/com/gkhy/assess/system/service/impl/CaptchaServiceImpl.java b/assess-system/src/main/java/com/gkhy/assess/system/service/impl/CaptchaServiceImpl.java
new file mode 100644
index 0000000..f128fef
--- /dev/null
+++ b/assess-system/src/main/java/com/gkhy/assess/system/service/impl/CaptchaServiceImpl.java
@@ -0,0 +1,60 @@
+package com.gkhy.assess.system.service.impl;
+
+import cn.hutool.core.codec.Base64;
+import com.gkhy.assess.common.constant.CacheConstant;
+import com.gkhy.assess.common.exception.ApiException;
+import com.gkhy.assess.common.utils.RedisUtils;
+import com.gkhy.assess.system.domain.vo.CaptchaVO;
+import com.gkhy.assess.system.service.CaptchaService;
+import com.google.code.kaptcha.Producer;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.util.FastByteArrayOutputStream;
+
+import javax.annotation.Resource;
+import javax.imageio.ImageIO;
+import java.awt.image.BufferedImage;
+import java.io.IOException;
+import java.util.UUID;
+import java.util.concurrent.TimeUnit;
+
+@Slf4j
+@Service
+public class CaptchaServiceImpl implements CaptchaService {
+    @Resource(name = "captchaProducer")
+    private Producer captchaProducer;
+
+    @Resource(name = "captchaProducerMath")
+    private Producer captchaProducerMath;
+    @Autowired
+    private RedisUtils redisUtils;
+
+    @Override
+    public CaptchaVO captchaImage() {
+        String uuid= UUID.randomUUID().toString();
+        String verifyKey= CacheConstant.CAPTCHA_CODE_KEY+uuid;
+        String capText=captchaProducerMath.createText();
+        String capStr=capText.substring(0,capText.lastIndexOf("@"));
+        String code=capText.substring(capText.lastIndexOf("@")+1);
+        BufferedImage image=captchaProducerMath.createImage(capStr);
+        redisUtils.set(verifyKey,code,2, TimeUnit.MINUTES);
+        // 转换流信息写出
+        FastByteArrayOutputStream os = new FastByteArrayOutputStream();
+        try
+        {
+            ImageIO.write(image, "jpg", os);
+        }
+        catch (IOException e)
+        {
+            log.error(e.getMessage());
+            throw new ApiException("生成验证码失败");
+        }
+        CaptchaVO captchaVO=new CaptchaVO();
+        captchaVO.setUuid(uuid);
+        captchaVO.setImage(Base64.encode(os.toByteArray()));
+        return captchaVO;
+    }
+
+
+}
diff --git a/assess-system/src/main/java/com/gkhy/assess/system/service/impl/SysUserServiceImpl.java b/assess-system/src/main/java/com/gkhy/assess/system/service/impl/SysUserServiceImpl.java
index 9bfc0cd..6ceaac1 100644
--- a/assess-system/src/main/java/com/gkhy/assess/system/service/impl/SysUserServiceImpl.java
+++ b/assess-system/src/main/java/com/gkhy/assess/system/service/impl/SysUserServiceImpl.java
@@ -1,6 +1,7 @@
 package com.gkhy.assess.system.service.impl;
 
 import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.core.util.StrUtil;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.gkhy.assess.common.api.CommonPage;
 import com.gkhy.assess.common.constant.CacheConstant;
@@ -57,6 +58,8 @@
 
     @Override
     public AccountVO login(LoginBody loginBody) {
+        // 验证码校验
+        validateCaptcha(loginBody.getUsername(), loginBody.getCode(), loginBody.getUuid());
         UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(loginBody.getUsername(), loginBody.getPassword(), false);
         Subject subject= SecurityUtils.getSubject();
         String msg ;
@@ -84,6 +87,32 @@
         }
     }
 
+    /**
+     * 校验验证码
+     *
+     * @param username 用户名
+     * @param code 验证码
+     * @param uuid 唯一标识
+     * @return 结果
+     */
+    public void validateCaptcha(String username, String code, String uuid)
+    {
+            if(StrUtil.isBlank(code)||StrUtil.isBlank(uuid)){
+                throw new ApiException("验证码或验证码标识为空");
+            }
+            String verifyKey = CacheConstant.CAPTCHA_CODE_KEY +uuid;
+            String captcha = (String) redisUtils.get(verifyKey);
+            redisUtils.del(verifyKey);
+            if (StrUtil.isBlank(captcha))
+            {
+                throw new ApiException("验证码已失效");
+            }
+            if (!code.equalsIgnoreCase(captcha))
+            {
+                throw new ApiException("验证码不正确");
+            }
+    }
+
     @Override
     public void logout() {
         String jwtToken = request.getHeader(JwtTokenUtil.USER_LOGIN_TOKEN);
diff --git a/assess-system/src/main/java/com/gkhy/assess/system/utils/ShiroUtils.java b/assess-system/src/main/java/com/gkhy/assess/system/utils/ShiroUtils.java
index 682c61b..4fcd1bf 100644
--- a/assess-system/src/main/java/com/gkhy/assess/system/utils/ShiroUtils.java
+++ b/assess-system/src/main/java/com/gkhy/assess/system/utils/ShiroUtils.java
@@ -25,7 +25,7 @@
 
     public static Long getUserId()
     {
-        return getSysUser().getId().longValue();
+        return getSysUser()!=null?getSysUser().getId().longValue():null;
     }
 
     public static SysUser getSysUser()
diff --git a/pom.xml b/pom.xml
index 82664af..ea92af6 100644
--- a/pom.xml
+++ b/pom.xml
@@ -39,6 +39,7 @@
         <java-jwt.version>3.11.0</java-jwt.version>
         <fastjson.version>1.2.76</fastjson.version>
         <caffeine.version>2.9.3</caffeine.version>
+        <kaptcha.version>2.3.2</kaptcha.version>
     </properties>
     <dependencyManagement>
         <dependencies>
@@ -143,6 +144,13 @@
                 <version>${caffeine.version}</version>
             </dependency>
 
+            <!-- 验证码 -->
+            <dependency>
+                <groupId>com.github.penggle</groupId>
+                <artifactId>kaptcha</artifactId>
+                <version>${kaptcha.version}</version>
+            </dependency>
+
         </dependencies>
     </dependencyManagement>
 

--
Gitblit v1.9.2