kongzy
2023-11-27 6ebe8da9b42e773f48260c6d1e0b90307bde5303
增加验证码
已修改9个文件
已添加7个文件
456 ■■■■■ 文件已修改
assess-admin/src/main/java/com/gkhy/assess/admin/controller/CaptchaController.java 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
assess-common/pom.xml 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
assess-common/src/main/java/com/gkhy/assess/common/config/CaptchaConfig.java 84 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
assess-common/src/main/java/com/gkhy/assess/common/config/KaptchaTextCreator.java 68 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
assess-common/src/main/java/com/gkhy/assess/common/constant/CacheConstant.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
assess-common/src/main/java/com/gkhy/assess/common/domain/vo/LoginBody.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
assess-framework/src/main/java/com/gkhy/assess/framework/aop/LogAspect.java 130 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
assess-framework/src/main/java/com/gkhy/assess/framework/config/DruidConfig.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
assess-framework/src/main/java/com/gkhy/assess/framework/interceptor/LogInterceptor.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
assess-framework/src/main/java/com/gkhy/assess/framework/shiro/ShiroConfig.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
assess-system/src/main/java/com/gkhy/assess/system/domain/vo/CaptchaVO.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
assess-system/src/main/java/com/gkhy/assess/system/service/CaptchaService.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
assess-system/src/main/java/com/gkhy/assess/system/service/impl/CaptchaServiceImpl.java 60 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
assess-system/src/main/java/com/gkhy/assess/system/service/impl/SysUserServiceImpl.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
assess-system/src/main/java/com/gkhy/assess/system/utils/ShiroUtils.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
pom.xml 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
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());
    }
}
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>
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;
    }
}
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();
    }
}
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:";
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;
}
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;
        }
    }
}
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);
            }
        };
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;
    }
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"); // 需登录才能访问
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;
}
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();
}
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;
    }
}
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);
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()
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>