mirror of
				https://github.com/dromara/RuoYi-Vue-Plus.git
				synced 2025-11-04 16:23:42 +08:00 
			
		
		
		
	update 使用hutool重写验证码生成
This commit is contained in:
		@@ -24,6 +24,7 @@
 | 
			
		||||
* 容器改动 Tomcat 改为 并发性能更好的 undertow
 | 
			
		||||
* 代码生成模板 改为适配 Mybatis-Plus 的代码
 | 
			
		||||
* 项目修改为 maven多环境配置
 | 
			
		||||
* 集成 Hutool 5.X 并重写RuoYi部分功能
 | 
			
		||||
* 集成 Feign 接口化管理 Http 请求(如三方请求 支付,短信,推送等)
 | 
			
		||||
* 升级MybatisPlus 3.4.2
 | 
			
		||||
* 增加demo模块示例(给不会增加模块的小伙伴做参考)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										10
									
								
								pom.xml
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								pom.xml
									
									
									
									
									
								
							@@ -21,7 +21,6 @@
 | 
			
		||||
        <druid.version>1.2.4</druid.version>
 | 
			
		||||
        <bitwalker.version>1.21</bitwalker.version>
 | 
			
		||||
        <swagger.version>2.9.2</swagger.version>
 | 
			
		||||
		<kaptcha.version>2.3.2</kaptcha.version>
 | 
			
		||||
        <pagehelper.boot.version>1.3.0</pagehelper.boot.version>
 | 
			
		||||
        <fastjson.version>1.2.75</fastjson.version>
 | 
			
		||||
        <oshi.version>5.6.0</oshi.version>
 | 
			
		||||
@@ -32,7 +31,7 @@
 | 
			
		||||
        <velocity.version>1.7</velocity.version>
 | 
			
		||||
        <jwt.version>0.9.1</jwt.version>
 | 
			
		||||
        <mybatis-plus.version>3.4.2</mybatis-plus.version>
 | 
			
		||||
        <hutool.version>5.4.0</hutool.version>
 | 
			
		||||
        <hutool.version>5.5.8</hutool.version>
 | 
			
		||||
        <feign.version>2.2.6.RELEASE</feign.version>
 | 
			
		||||
        <feign-okhttp.version>11.0</feign-okhttp.version>
 | 
			
		||||
    </properties>
 | 
			
		||||
@@ -162,13 +161,6 @@
 | 
			
		||||
                <version>${jwt.version}</version>
 | 
			
		||||
            </dependency>
 | 
			
		||||
 | 
			
		||||
            <!--验证码 -->
 | 
			
		||||
            <dependency>
 | 
			
		||||
                <groupId>com.github.penggle</groupId>
 | 
			
		||||
                <artifactId>kaptcha</artifactId>
 | 
			
		||||
                <version>${kaptcha.version}</version>
 | 
			
		||||
            </dependency>
 | 
			
		||||
 | 
			
		||||
            <dependency>
 | 
			
		||||
                <groupId>com.baomidou</groupId>
 | 
			
		||||
                <artifactId>mybatis-plus-boot-starter</artifactId>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,86 +1,120 @@
 | 
			
		||||
package com.ruoyi.web.controller.common;
 | 
			
		||||
 | 
			
		||||
import java.awt.image.BufferedImage;
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.util.concurrent.TimeUnit;
 | 
			
		||||
import javax.annotation.Resource;
 | 
			
		||||
import javax.imageio.ImageIO;
 | 
			
		||||
import javax.servlet.http.HttpServletResponse;
 | 
			
		||||
 | 
			
		||||
import cn.hutool.captcha.CircleCaptcha;
 | 
			
		||||
import cn.hutool.captcha.ICaptcha;
 | 
			
		||||
import cn.hutool.captcha.LineCaptcha;
 | 
			
		||||
import cn.hutool.captcha.ShearCaptcha;
 | 
			
		||||
import cn.hutool.captcha.generator.CodeGenerator;
 | 
			
		||||
import cn.hutool.captcha.generator.MathGenerator;
 | 
			
		||||
import cn.hutool.captcha.generator.RandomGenerator;
 | 
			
		||||
import cn.hutool.core.util.IdUtil;
 | 
			
		||||
import cn.hutool.core.util.StrUtil;
 | 
			
		||||
import com.ruoyi.common.core.text.Convert;
 | 
			
		||||
import org.springframework.beans.factory.annotation.Autowired;
 | 
			
		||||
import org.springframework.beans.factory.annotation.Value;
 | 
			
		||||
import org.springframework.util.FastByteArrayOutputStream;
 | 
			
		||||
import org.springframework.web.bind.annotation.GetMapping;
 | 
			
		||||
import org.springframework.web.bind.annotation.RestController;
 | 
			
		||||
import com.google.code.kaptcha.Producer;
 | 
			
		||||
import com.ruoyi.common.constant.Constants;
 | 
			
		||||
import com.ruoyi.common.core.domain.AjaxResult;
 | 
			
		||||
import com.ruoyi.common.core.redis.RedisCache;
 | 
			
		||||
import com.ruoyi.common.utils.sign.Base64;
 | 
			
		||||
import com.ruoyi.common.utils.uuid.IdUtils;
 | 
			
		||||
 | 
			
		||||
import javax.annotation.Resource;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 验证码操作处理
 | 
			
		||||
 * 
 | 
			
		||||
 * @author ruoyi
 | 
			
		||||
 *
 | 
			
		||||
 * @author Lion Li
 | 
			
		||||
 */
 | 
			
		||||
@RestController
 | 
			
		||||
public class CaptchaController
 | 
			
		||||
{
 | 
			
		||||
    @Resource(name = "captchaProducer")
 | 
			
		||||
    private Producer captchaProducer;
 | 
			
		||||
public class CaptchaController {
 | 
			
		||||
 | 
			
		||||
    @Resource(name = "captchaProducerMath")
 | 
			
		||||
    private Producer captchaProducerMath;
 | 
			
		||||
    // 圆圈干扰验证码
 | 
			
		||||
    @Resource(name = "CircleCaptcha")
 | 
			
		||||
    private CircleCaptcha circleCaptcha;
 | 
			
		||||
    // 线段干扰的验证码
 | 
			
		||||
    @Resource(name = "LineCaptcha")
 | 
			
		||||
    private LineCaptcha lineCaptcha;
 | 
			
		||||
    // 扭曲干扰验证码
 | 
			
		||||
    @Resource(name = "ShearCaptcha")
 | 
			
		||||
    private ShearCaptcha shearCaptcha;
 | 
			
		||||
 | 
			
		||||
    @Autowired
 | 
			
		||||
    private RedisCache redisCache;
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    // 验证码类型
 | 
			
		||||
    @Value("${ruoyi.captchaType}")
 | 
			
		||||
    @Value("${captcha.captchaType}")
 | 
			
		||||
    private String captchaType;
 | 
			
		||||
    // 验证码类别
 | 
			
		||||
    @Value("${captcha.captchaCategory}")
 | 
			
		||||
    private String captchaCategory;
 | 
			
		||||
    // 数字验证码位数
 | 
			
		||||
    @Value("${captcha.captchaNumberLength}")
 | 
			
		||||
    private int numberLength;
 | 
			
		||||
    // 字符验证码长度
 | 
			
		||||
    @Value("${captcha.captchaCharLength}")
 | 
			
		||||
    private int charLength;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 生成验证码
 | 
			
		||||
     */
 | 
			
		||||
    @GetMapping("/captchaImage")
 | 
			
		||||
    public AjaxResult getCode(HttpServletResponse response) throws IOException
 | 
			
		||||
    {
 | 
			
		||||
    public AjaxResult getCode() {
 | 
			
		||||
        // 保存验证码信息
 | 
			
		||||
        String uuid = IdUtils.simpleUUID();
 | 
			
		||||
        String uuid = IdUtil.simpleUUID();
 | 
			
		||||
        String verifyKey = Constants.CAPTCHA_CODE_KEY + uuid;
 | 
			
		||||
 | 
			
		||||
        String capStr = null, code = null;
 | 
			
		||||
        BufferedImage image = null;
 | 
			
		||||
 | 
			
		||||
        // 生成验证码
 | 
			
		||||
        if ("math".equals(captchaType))
 | 
			
		||||
        {
 | 
			
		||||
            String capText = captchaProducerMath.createText();
 | 
			
		||||
            capStr = capText.substring(0, capText.lastIndexOf("@"));
 | 
			
		||||
            code = capText.substring(capText.lastIndexOf("@") + 1);
 | 
			
		||||
            image = captchaProducerMath.createImage(capStr);
 | 
			
		||||
        CodeGenerator codeGenerator;
 | 
			
		||||
        if ("math".equals(captchaType)) {
 | 
			
		||||
            codeGenerator = new MathGenerator(numberLength);
 | 
			
		||||
        } else if ("char".equals(captchaType)) {
 | 
			
		||||
            codeGenerator = new RandomGenerator(charLength);
 | 
			
		||||
        } else {
 | 
			
		||||
            throw new IllegalArgumentException("验证码类型异常");
 | 
			
		||||
        }
 | 
			
		||||
        else if ("char".equals(captchaType))
 | 
			
		||||
        {
 | 
			
		||||
            capStr = code = captchaProducer.createText();
 | 
			
		||||
            image = captchaProducer.createImage(capStr);
 | 
			
		||||
        if ("line".equals(captchaCategory)) {
 | 
			
		||||
            lineCaptcha.setGenerator(codeGenerator);
 | 
			
		||||
            capStr = lineCaptcha.getCode();
 | 
			
		||||
        } else if ("circle".equals(captchaCategory)) {
 | 
			
		||||
            circleCaptcha.setGenerator(codeGenerator);
 | 
			
		||||
            capStr = circleCaptcha.getCode();
 | 
			
		||||
        } else if ("shear".equals(captchaCategory)) {
 | 
			
		||||
            shearCaptcha.setGenerator(codeGenerator);
 | 
			
		||||
            capStr = shearCaptcha.getCode();
 | 
			
		||||
        }  else {
 | 
			
		||||
            throw new IllegalArgumentException("验证码类别异常");
 | 
			
		||||
        }
 | 
			
		||||
        if ("math".equals(captchaType)) {
 | 
			
		||||
            code = getCodeResult(capStr);
 | 
			
		||||
        } else if ("char".equals(captchaType)) {
 | 
			
		||||
            code = capStr;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        redisCache.setCacheObject(verifyKey, code, Constants.CAPTCHA_EXPIRATION, TimeUnit.MINUTES);
 | 
			
		||||
        // 转换流信息写出
 | 
			
		||||
        FastByteArrayOutputStream os = new FastByteArrayOutputStream();
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            ImageIO.write(image, "jpg", os);
 | 
			
		||||
        }
 | 
			
		||||
        catch (IOException e)
 | 
			
		||||
        {
 | 
			
		||||
            return AjaxResult.error(e.getMessage());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        circleCaptcha.createImage(capStr);
 | 
			
		||||
        AjaxResult ajax = AjaxResult.success();
 | 
			
		||||
        ajax.put("uuid", uuid);
 | 
			
		||||
        ajax.put("img", Base64.encode(os.toByteArray()));
 | 
			
		||||
        ajax.put("img", circleCaptcha.getImageBase64());
 | 
			
		||||
        return ajax;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private String getCodeResult(String capStr) {
 | 
			
		||||
        int a = Convert.toInt(StrUtil.sub(capStr, 0, numberLength).trim());
 | 
			
		||||
        char operator = capStr.charAt(numberLength);
 | 
			
		||||
        int b = Convert.toInt(StrUtil.sub(capStr, numberLength + 1, numberLength + 1 + numberLength).trim());
 | 
			
		||||
        switch (operator) {
 | 
			
		||||
            case '*':
 | 
			
		||||
                return a * b + "";
 | 
			
		||||
            case '+':
 | 
			
		||||
                return a + b + "";
 | 
			
		||||
            case '-':
 | 
			
		||||
                return a - b + "";
 | 
			
		||||
            default:
 | 
			
		||||
                return "";
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -12,8 +12,16 @@ ruoyi:
 | 
			
		||||
  profile: ${user.dir}/ruoyi/uploadPath
 | 
			
		||||
  # 获取ip地址开关
 | 
			
		||||
  addressEnabled: false
 | 
			
		||||
 | 
			
		||||
captcha:
 | 
			
		||||
  # 验证码类型 math 数组计算 char 字符验证
 | 
			
		||||
  captchaType: math
 | 
			
		||||
  # line 线段干扰 circle 圆圈干扰 shear 扭曲干扰
 | 
			
		||||
  captchaCategory: circle
 | 
			
		||||
  # 数字验证码位数
 | 
			
		||||
  captchaNumberLength: 1
 | 
			
		||||
  # 字符验证码长度
 | 
			
		||||
  captchaCharLength: 4
 | 
			
		||||
 | 
			
		||||
# 开发环境配置
 | 
			
		||||
server:
 | 
			
		||||
 
 | 
			
		||||
@@ -1,228 +0,0 @@
 | 
			
		||||
package com.ruoyi.common.utils;
 | 
			
		||||
 | 
			
		||||
import java.awt.Color;
 | 
			
		||||
import java.awt.Font;
 | 
			
		||||
import java.awt.Graphics;
 | 
			
		||||
import java.awt.Graphics2D;
 | 
			
		||||
import java.awt.RenderingHints;
 | 
			
		||||
import java.awt.geom.AffineTransform;
 | 
			
		||||
import java.awt.image.BufferedImage;
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.io.OutputStream;
 | 
			
		||||
import java.security.SecureRandom;
 | 
			
		||||
import java.util.Arrays;
 | 
			
		||||
import java.util.Random;
 | 
			
		||||
import javax.imageio.ImageIO;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 验证码工具类
 | 
			
		||||
 * 
 | 
			
		||||
 * @author ruoyi
 | 
			
		||||
 */
 | 
			
		||||
public class VerifyCodeUtils
 | 
			
		||||
{
 | 
			
		||||
    // 使用到Algerian字体,系统里没有的话需要安装字体,字体只显示大写,去掉了1,0,i,o几个容易混淆的字符
 | 
			
		||||
    public static final String VERIFY_CODES = "123456789ABCDEFGHJKLMNPQRSTUVWXYZ";
 | 
			
		||||
 | 
			
		||||
    private static Random random = new SecureRandom();
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 使用系统默认字符源生成验证码
 | 
			
		||||
     * 
 | 
			
		||||
     * @param verifySize 验证码长度
 | 
			
		||||
     * @return
 | 
			
		||||
     */
 | 
			
		||||
    public static String generateVerifyCode(int verifySize)
 | 
			
		||||
    {
 | 
			
		||||
        return generateVerifyCode(verifySize, VERIFY_CODES);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 使用指定源生成验证码
 | 
			
		||||
     * 
 | 
			
		||||
     * @param verifySize 验证码长度
 | 
			
		||||
     * @param sources 验证码字符源
 | 
			
		||||
     * @return
 | 
			
		||||
     */
 | 
			
		||||
    public static String generateVerifyCode(int verifySize, String sources)
 | 
			
		||||
    {
 | 
			
		||||
        if (sources == null || sources.length() == 0)
 | 
			
		||||
        {
 | 
			
		||||
            sources = VERIFY_CODES;
 | 
			
		||||
        }
 | 
			
		||||
        int codesLen = sources.length();
 | 
			
		||||
        Random rand = new Random(System.currentTimeMillis());
 | 
			
		||||
        StringBuilder verifyCode = new StringBuilder(verifySize);
 | 
			
		||||
        for (int i = 0; i < verifySize; i++)
 | 
			
		||||
        {
 | 
			
		||||
            verifyCode.append(sources.charAt(rand.nextInt(codesLen - 1)));
 | 
			
		||||
        }
 | 
			
		||||
        return verifyCode.toString();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 输出指定验证码图片流
 | 
			
		||||
     * 
 | 
			
		||||
     * @param w
 | 
			
		||||
     * @param h
 | 
			
		||||
     * @param os
 | 
			
		||||
     * @param code
 | 
			
		||||
     * @throws IOException
 | 
			
		||||
     */
 | 
			
		||||
    public static void outputImage(int w, int h, OutputStream os, String code) throws IOException
 | 
			
		||||
    {
 | 
			
		||||
        int verifySize = code.length();
 | 
			
		||||
        BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
 | 
			
		||||
        Random rand = new Random();
 | 
			
		||||
        Graphics2D g2 = image.createGraphics();
 | 
			
		||||
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
 | 
			
		||||
        Color[] colors = new Color[5];
 | 
			
		||||
        Color[] colorSpaces = new Color[] { Color.WHITE, Color.CYAN, Color.GRAY, Color.LIGHT_GRAY, Color.MAGENTA,
 | 
			
		||||
                Color.ORANGE, Color.PINK, Color.YELLOW };
 | 
			
		||||
        float[] fractions = new float[colors.length];
 | 
			
		||||
        for (int i = 0; i < colors.length; i++)
 | 
			
		||||
        {
 | 
			
		||||
            colors[i] = colorSpaces[rand.nextInt(colorSpaces.length)];
 | 
			
		||||
            fractions[i] = rand.nextFloat();
 | 
			
		||||
        }
 | 
			
		||||
        Arrays.sort(fractions);
 | 
			
		||||
 | 
			
		||||
        g2.setColor(Color.GRAY);// 设置边框色
 | 
			
		||||
        g2.fillRect(0, 0, w, h);
 | 
			
		||||
 | 
			
		||||
        Color c = getRandColor(200, 250);
 | 
			
		||||
        g2.setColor(c);// 设置背景色
 | 
			
		||||
        g2.fillRect(0, 2, w, h - 4);
 | 
			
		||||
 | 
			
		||||
        // 绘制干扰线
 | 
			
		||||
        Random random = new Random();
 | 
			
		||||
        g2.setColor(getRandColor(160, 200));// 设置线条的颜色
 | 
			
		||||
        for (int i = 0; i < 20; i++)
 | 
			
		||||
        {
 | 
			
		||||
            int x = random.nextInt(w - 1);
 | 
			
		||||
            int y = random.nextInt(h - 1);
 | 
			
		||||
            int xl = random.nextInt(6) + 1;
 | 
			
		||||
            int yl = random.nextInt(12) + 1;
 | 
			
		||||
            g2.drawLine(x, y, x + xl + 40, y + yl + 20);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 添加噪点
 | 
			
		||||
        float yawpRate = 0.05f;// 噪声率
 | 
			
		||||
        int area = (int) (yawpRate * w * h);
 | 
			
		||||
        for (int i = 0; i < area; i++)
 | 
			
		||||
        {
 | 
			
		||||
            int x = random.nextInt(w);
 | 
			
		||||
            int y = random.nextInt(h);
 | 
			
		||||
            int rgb = getRandomIntColor();
 | 
			
		||||
            image.setRGB(x, y, rgb);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        shear(g2, w, h, c);// 使图片扭曲
 | 
			
		||||
 | 
			
		||||
        g2.setColor(getRandColor(100, 160));
 | 
			
		||||
        int fontSize = h - 4;
 | 
			
		||||
        Font font = new Font("Algerian", Font.ITALIC, fontSize);
 | 
			
		||||
        g2.setFont(font);
 | 
			
		||||
        char[] chars = code.toCharArray();
 | 
			
		||||
        for (int i = 0; i < verifySize; i++)
 | 
			
		||||
        {
 | 
			
		||||
            AffineTransform affine = new AffineTransform();
 | 
			
		||||
            affine.setToRotation(Math.PI / 4 * rand.nextDouble() * (rand.nextBoolean() ? 1 : -1),
 | 
			
		||||
                    (w / verifySize) * i + fontSize / 2, h / 2);
 | 
			
		||||
            g2.setTransform(affine);
 | 
			
		||||
            g2.drawChars(chars, i, 1, ((w - 10) / verifySize) * i + 5, h / 2 + fontSize / 2 - 10);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        g2.dispose();
 | 
			
		||||
        ImageIO.write(image, "jpg", os);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static Color getRandColor(int fc, int bc)
 | 
			
		||||
    {
 | 
			
		||||
        if (fc > 255) {
 | 
			
		||||
            fc = 255;
 | 
			
		||||
        }
 | 
			
		||||
        if (bc > 255) {
 | 
			
		||||
            bc = 255;
 | 
			
		||||
        }
 | 
			
		||||
        int r = fc + random.nextInt(bc - fc);
 | 
			
		||||
        int g = fc + random.nextInt(bc - fc);
 | 
			
		||||
        int b = fc + random.nextInt(bc - fc);
 | 
			
		||||
        return new Color(r, g, b);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static int getRandomIntColor()
 | 
			
		||||
    {
 | 
			
		||||
        int[] rgb = getRandomRgb();
 | 
			
		||||
        int color = 0;
 | 
			
		||||
        for (int c : rgb)
 | 
			
		||||
        {
 | 
			
		||||
            color = color << 8;
 | 
			
		||||
            color = color | c;
 | 
			
		||||
        }
 | 
			
		||||
        return color;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static int[] getRandomRgb()
 | 
			
		||||
    {
 | 
			
		||||
        int[] rgb = new int[3];
 | 
			
		||||
        for (int i = 0; i < 3; i++)
 | 
			
		||||
        {
 | 
			
		||||
            rgb[i] = random.nextInt(255);
 | 
			
		||||
        }
 | 
			
		||||
        return rgb;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static void shear(Graphics g, int w1, int h1, Color color)
 | 
			
		||||
    {
 | 
			
		||||
        shearX(g, w1, h1, color);
 | 
			
		||||
        shearY(g, w1, h1, color);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static void shearX(Graphics g, int w1, int h1, Color color)
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        int period = random.nextInt(2);
 | 
			
		||||
 | 
			
		||||
        boolean borderGap = true;
 | 
			
		||||
        int frames = 1;
 | 
			
		||||
        int phase = random.nextInt(2);
 | 
			
		||||
 | 
			
		||||
        for (int i = 0; i < h1; i++)
 | 
			
		||||
        {
 | 
			
		||||
            double d = (double) (period >> 1)
 | 
			
		||||
                    * Math.sin((double) i / (double) period + (6.2831853071795862D * (double) phase) / (double) frames);
 | 
			
		||||
            g.copyArea(0, i, w1, 1, (int) d, 0);
 | 
			
		||||
            if (borderGap)
 | 
			
		||||
            {
 | 
			
		||||
                g.setColor(color);
 | 
			
		||||
                g.drawLine((int) d, i, 0, i);
 | 
			
		||||
                g.drawLine((int) d + w1, i, w1, i);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static void shearY(Graphics g, int w1, int h1, Color color)
 | 
			
		||||
    {
 | 
			
		||||
 | 
			
		||||
        int period = random.nextInt(40) + 10; // 50;
 | 
			
		||||
 | 
			
		||||
        boolean borderGap = true;
 | 
			
		||||
        int frames = 20;
 | 
			
		||||
        int phase = 7;
 | 
			
		||||
        for (int i = 0; i < w1; i++)
 | 
			
		||||
        {
 | 
			
		||||
            double d = (double) (period >> 1)
 | 
			
		||||
                    * Math.sin((double) i / (double) period + (6.2831853071795862D * (double) phase) / (double) frames);
 | 
			
		||||
            g.copyArea(i, 0, 1, h1, 0, (int) d);
 | 
			
		||||
            if (borderGap)
 | 
			
		||||
            {
 | 
			
		||||
                g.setColor(color);
 | 
			
		||||
                g.drawLine(i, (int) d, i, 0);
 | 
			
		||||
                g.drawLine(i, (int) d + h1, i, h1);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -2,6 +2,8 @@ package com.ruoyi.common.utils.file;
 | 
			
		||||
 | 
			
		||||
import java.io.File;
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
 | 
			
		||||
import cn.hutool.core.util.IdUtil;
 | 
			
		||||
import org.apache.commons.io.FilenameUtils;
 | 
			
		||||
import org.springframework.web.multipart.MultipartFile;
 | 
			
		||||
import com.ruoyi.common.config.RuoYiConfig;
 | 
			
		||||
@@ -11,7 +13,6 @@ import com.ruoyi.common.exception.file.FileSizeLimitExceededException;
 | 
			
		||||
import com.ruoyi.common.exception.file.InvalidExtensionException;
 | 
			
		||||
import com.ruoyi.common.utils.DateUtils;
 | 
			
		||||
import com.ruoyi.common.utils.StringUtils;
 | 
			
		||||
import com.ruoyi.common.utils.uuid.IdUtils;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 文件上传工具类
 | 
			
		||||
@@ -123,7 +124,7 @@ public class FileUploadUtils
 | 
			
		||||
    {
 | 
			
		||||
        String fileName = file.getOriginalFilename();
 | 
			
		||||
        String extension = getExtension(file);
 | 
			
		||||
        fileName = DateUtils.datePath() + "/" + IdUtils.fastUUID() + "." + extension;
 | 
			
		||||
        fileName = DateUtils.datePath() + "/" + IdUtil.fastUUID() + "." + extension;
 | 
			
		||||
        return fileName;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,51 +0,0 @@
 | 
			
		||||
package com.ruoyi.common.utils.uuid;
 | 
			
		||||
 | 
			
		||||
import com.ruoyi.common.utils.uuid.UUID;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * ID生成器工具类
 | 
			
		||||
 * 
 | 
			
		||||
 * @author ruoyi
 | 
			
		||||
 */
 | 
			
		||||
public class IdUtils
 | 
			
		||||
{
 | 
			
		||||
    /**
 | 
			
		||||
     * 获取随机UUID
 | 
			
		||||
     * 
 | 
			
		||||
     * @return 随机UUID
 | 
			
		||||
     */
 | 
			
		||||
    public static String randomUUID()
 | 
			
		||||
    {
 | 
			
		||||
        return UUID.randomUUID().toString();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 简化的UUID,去掉了横线
 | 
			
		||||
     * 
 | 
			
		||||
     * @return 简化的UUID,去掉了横线
 | 
			
		||||
     */
 | 
			
		||||
    public static String simpleUUID()
 | 
			
		||||
    {
 | 
			
		||||
        return UUID.randomUUID().toString(true);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获取随机UUID,使用性能更好的ThreadLocalRandom生成UUID
 | 
			
		||||
     * 
 | 
			
		||||
     * @return 随机UUID
 | 
			
		||||
     */
 | 
			
		||||
    public static String fastUUID()
 | 
			
		||||
    {
 | 
			
		||||
        return UUID.fastUUID().toString();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 简化的UUID,去掉了横线,使用性能更好的ThreadLocalRandom生成UUID
 | 
			
		||||
     * 
 | 
			
		||||
     * @return 简化的UUID,去掉了横线
 | 
			
		||||
     */
 | 
			
		||||
    public static String fastSimpleUUID()
 | 
			
		||||
    {
 | 
			
		||||
        return UUID.fastUUID().toString(true);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,484 +0,0 @@
 | 
			
		||||
package com.ruoyi.common.utils.uuid;
 | 
			
		||||
 | 
			
		||||
import java.security.MessageDigest;
 | 
			
		||||
import java.security.NoSuchAlgorithmException;
 | 
			
		||||
import java.security.SecureRandom;
 | 
			
		||||
import java.util.Random;
 | 
			
		||||
import java.util.concurrent.ThreadLocalRandom;
 | 
			
		||||
import com.ruoyi.common.exception.UtilException;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 提供通用唯一识别码(universally unique identifier)(UUID)实现
 | 
			
		||||
 *
 | 
			
		||||
 * @author ruoyi
 | 
			
		||||
 */
 | 
			
		||||
public final class UUID implements java.io.Serializable, Comparable<UUID>
 | 
			
		||||
{
 | 
			
		||||
    private static final long serialVersionUID = -1185015143654744140L;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * SecureRandom 的单例
 | 
			
		||||
     *
 | 
			
		||||
     */
 | 
			
		||||
    private static class Holder
 | 
			
		||||
    {
 | 
			
		||||
        static final SecureRandom numberGenerator = getSecureRandom();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /** 此UUID的最高64有效位 */
 | 
			
		||||
    private final long mostSigBits;
 | 
			
		||||
 | 
			
		||||
    /** 此UUID的最低64有效位 */
 | 
			
		||||
    private final long leastSigBits;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 私有构造
 | 
			
		||||
     * 
 | 
			
		||||
     * @param data 数据
 | 
			
		||||
     */
 | 
			
		||||
    private UUID(byte[] data)
 | 
			
		||||
    {
 | 
			
		||||
        long msb = 0;
 | 
			
		||||
        long lsb = 0;
 | 
			
		||||
        assert data.length == 16 : "data must be 16 bytes in length";
 | 
			
		||||
        for (int i = 0; i < 8; i++)
 | 
			
		||||
        {
 | 
			
		||||
            msb = (msb << 8) | (data[i] & 0xff);
 | 
			
		||||
        }
 | 
			
		||||
        for (int i = 8; i < 16; i++)
 | 
			
		||||
        {
 | 
			
		||||
            lsb = (lsb << 8) | (data[i] & 0xff);
 | 
			
		||||
        }
 | 
			
		||||
        this.mostSigBits = msb;
 | 
			
		||||
        this.leastSigBits = lsb;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 使用指定的数据构造新的 UUID。
 | 
			
		||||
     *
 | 
			
		||||
     * @param mostSigBits 用于 {@code UUID} 的最高有效 64 位
 | 
			
		||||
     * @param leastSigBits 用于 {@code UUID} 的最低有效 64 位
 | 
			
		||||
     */
 | 
			
		||||
    public UUID(long mostSigBits, long leastSigBits)
 | 
			
		||||
    {
 | 
			
		||||
        this.mostSigBits = mostSigBits;
 | 
			
		||||
        this.leastSigBits = leastSigBits;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获取类型 4(伪随机生成的)UUID 的静态工厂。 使用加密的本地线程伪随机数生成器生成该 UUID。
 | 
			
		||||
     * 
 | 
			
		||||
     * @return 随机生成的 {@code UUID}
 | 
			
		||||
     */
 | 
			
		||||
    public static UUID fastUUID()
 | 
			
		||||
    {
 | 
			
		||||
        return randomUUID(false);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获取类型 4(伪随机生成的)UUID 的静态工厂。 使用加密的强伪随机数生成器生成该 UUID。
 | 
			
		||||
     * 
 | 
			
		||||
     * @return 随机生成的 {@code UUID}
 | 
			
		||||
     */
 | 
			
		||||
    public static UUID randomUUID()
 | 
			
		||||
    {
 | 
			
		||||
        return randomUUID(true);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获取类型 4(伪随机生成的)UUID 的静态工厂。 使用加密的强伪随机数生成器生成该 UUID。
 | 
			
		||||
     * 
 | 
			
		||||
     * @param isSecure 是否使用{@link SecureRandom}如果是可以获得更安全的随机码,否则可以得到更好的性能
 | 
			
		||||
     * @return 随机生成的 {@code UUID}
 | 
			
		||||
     */
 | 
			
		||||
    public static UUID randomUUID(boolean isSecure)
 | 
			
		||||
    {
 | 
			
		||||
        final Random ng = isSecure ? Holder.numberGenerator : getRandom();
 | 
			
		||||
 | 
			
		||||
        byte[] randomBytes = new byte[16];
 | 
			
		||||
        ng.nextBytes(randomBytes);
 | 
			
		||||
        randomBytes[6] &= 0x0f; /* clear version */
 | 
			
		||||
        randomBytes[6] |= 0x40; /* set to version 4 */
 | 
			
		||||
        randomBytes[8] &= 0x3f; /* clear variant */
 | 
			
		||||
        randomBytes[8] |= 0x80; /* set to IETF variant */
 | 
			
		||||
        return new UUID(randomBytes);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 根据指定的字节数组获取类型 3(基于名称的)UUID 的静态工厂。
 | 
			
		||||
     *
 | 
			
		||||
     * @param name 用于构造 UUID 的字节数组。
 | 
			
		||||
     *
 | 
			
		||||
     * @return 根据指定数组生成的 {@code UUID}
 | 
			
		||||
     */
 | 
			
		||||
    public static UUID nameUUIDFromBytes(byte[] name)
 | 
			
		||||
    {
 | 
			
		||||
        MessageDigest md;
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            md = MessageDigest.getInstance("MD5");
 | 
			
		||||
        }
 | 
			
		||||
        catch (NoSuchAlgorithmException nsae)
 | 
			
		||||
        {
 | 
			
		||||
            throw new InternalError("MD5 not supported");
 | 
			
		||||
        }
 | 
			
		||||
        byte[] md5Bytes = md.digest(name);
 | 
			
		||||
        md5Bytes[6] &= 0x0f; /* clear version */
 | 
			
		||||
        md5Bytes[6] |= 0x30; /* set to version 3 */
 | 
			
		||||
        md5Bytes[8] &= 0x3f; /* clear variant */
 | 
			
		||||
        md5Bytes[8] |= 0x80; /* set to IETF variant */
 | 
			
		||||
        return new UUID(md5Bytes);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 根据 {@link #toString()} 方法中描述的字符串标准表示形式创建{@code UUID}。
 | 
			
		||||
     *
 | 
			
		||||
     * @param name 指定 {@code UUID} 字符串
 | 
			
		||||
     * @return 具有指定值的 {@code UUID}
 | 
			
		||||
     * @throws IllegalArgumentException 如果 name 与 {@link #toString} 中描述的字符串表示形式不符抛出此异常
 | 
			
		||||
     *
 | 
			
		||||
     */
 | 
			
		||||
    public static UUID fromString(String name)
 | 
			
		||||
    {
 | 
			
		||||
        String[] components = name.split("-");
 | 
			
		||||
        if (components.length != 5)
 | 
			
		||||
        {
 | 
			
		||||
            throw new IllegalArgumentException("Invalid UUID string: " + name);
 | 
			
		||||
        }
 | 
			
		||||
        for (int i = 0; i < 5; i++)
 | 
			
		||||
        {
 | 
			
		||||
            components[i] = "0x" + components[i];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        long mostSigBits = Long.decode(components[0]).longValue();
 | 
			
		||||
        mostSigBits <<= 16;
 | 
			
		||||
        mostSigBits |= Long.decode(components[1]).longValue();
 | 
			
		||||
        mostSigBits <<= 16;
 | 
			
		||||
        mostSigBits |= Long.decode(components[2]).longValue();
 | 
			
		||||
 | 
			
		||||
        long leastSigBits = Long.decode(components[3]).longValue();
 | 
			
		||||
        leastSigBits <<= 48;
 | 
			
		||||
        leastSigBits |= Long.decode(components[4]).longValue();
 | 
			
		||||
 | 
			
		||||
        return new UUID(mostSigBits, leastSigBits);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 返回此 UUID 的 128 位值中的最低有效 64 位。
 | 
			
		||||
     *
 | 
			
		||||
     * @return 此 UUID 的 128 位值中的最低有效 64 位。
 | 
			
		||||
     */
 | 
			
		||||
    public long getLeastSignificantBits()
 | 
			
		||||
    {
 | 
			
		||||
        return leastSigBits;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 返回此 UUID 的 128 位值中的最高有效 64 位。
 | 
			
		||||
     *
 | 
			
		||||
     * @return 此 UUID 的 128 位值中最高有效 64 位。
 | 
			
		||||
     */
 | 
			
		||||
    public long getMostSignificantBits()
 | 
			
		||||
    {
 | 
			
		||||
        return mostSigBits;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 与此 {@code UUID} 相关联的版本号. 版本号描述此 {@code UUID} 是如何生成的。
 | 
			
		||||
     * <p>
 | 
			
		||||
     * 版本号具有以下含意:
 | 
			
		||||
     * <ul>
 | 
			
		||||
     * <li>1 基于时间的 UUID
 | 
			
		||||
     * <li>2 DCE 安全 UUID
 | 
			
		||||
     * <li>3 基于名称的 UUID
 | 
			
		||||
     * <li>4 随机生成的 UUID
 | 
			
		||||
     * </ul>
 | 
			
		||||
     *
 | 
			
		||||
     * @return 此 {@code UUID} 的版本号
 | 
			
		||||
     */
 | 
			
		||||
    public int version()
 | 
			
		||||
    {
 | 
			
		||||
        // Version is bits masked by 0x000000000000F000 in MS long
 | 
			
		||||
        return (int) ((mostSigBits >> 12) & 0x0f);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 与此 {@code UUID} 相关联的变体号。变体号描述 {@code UUID} 的布局。
 | 
			
		||||
     * <p>
 | 
			
		||||
     * 变体号具有以下含意:
 | 
			
		||||
     * <ul>
 | 
			
		||||
     * <li>0 为 NCS 向后兼容保留
 | 
			
		||||
     * <li>2 <a href="http://www.ietf.org/rfc/rfc4122.txt">IETF RFC 4122</a>(Leach-Salz), 用于此类
 | 
			
		||||
     * <li>6 保留,微软向后兼容
 | 
			
		||||
     * <li>7 保留供以后定义使用
 | 
			
		||||
     * </ul>
 | 
			
		||||
     *
 | 
			
		||||
     * @return 此 {@code UUID} 相关联的变体号
 | 
			
		||||
     */
 | 
			
		||||
    public int variant()
 | 
			
		||||
    {
 | 
			
		||||
        // This field is composed of a varying number of bits.
 | 
			
		||||
        // 0 - - Reserved for NCS backward compatibility
 | 
			
		||||
        // 1 0 - The IETF aka Leach-Salz variant (used by this class)
 | 
			
		||||
        // 1 1 0 Reserved, Microsoft backward compatibility
 | 
			
		||||
        // 1 1 1 Reserved for future definition.
 | 
			
		||||
        return (int) ((leastSigBits >>> (64 - (leastSigBits >>> 62))) & (leastSigBits >> 63));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 与此 UUID 相关联的时间戳值。
 | 
			
		||||
     *
 | 
			
		||||
     * <p>
 | 
			
		||||
     * 60 位的时间戳值根据此 {@code UUID} 的 time_low、time_mid 和 time_hi 字段构造。<br>
 | 
			
		||||
     * 所得到的时间戳以 100 毫微秒为单位,从 UTC(通用协调时间) 1582 年 10 月 15 日零时开始。
 | 
			
		||||
     *
 | 
			
		||||
     * <p>
 | 
			
		||||
     * 时间戳值仅在在基于时间的 UUID(其 version 类型为 1)中才有意义。<br>
 | 
			
		||||
     * 如果此 {@code UUID} 不是基于时间的 UUID,则此方法抛出 UnsupportedOperationException。
 | 
			
		||||
     *
 | 
			
		||||
     * @throws UnsupportedOperationException 如果此 {@code UUID} 不是 version 为 1 的 UUID。
 | 
			
		||||
     */
 | 
			
		||||
    public long timestamp() throws UnsupportedOperationException
 | 
			
		||||
    {
 | 
			
		||||
        checkTimeBase();
 | 
			
		||||
        return (mostSigBits & 0x0FFFL) << 48//
 | 
			
		||||
                | ((mostSigBits >> 16) & 0x0FFFFL) << 32//
 | 
			
		||||
                | mostSigBits >>> 32;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 与此 UUID 相关联的时钟序列值。
 | 
			
		||||
     *
 | 
			
		||||
     * <p>
 | 
			
		||||
     * 14 位的时钟序列值根据此 UUID 的 clock_seq 字段构造。clock_seq 字段用于保证在基于时间的 UUID 中的时间唯一性。
 | 
			
		||||
     * <p>
 | 
			
		||||
     * {@code clockSequence} 值仅在基于时间的 UUID(其 version 类型为 1)中才有意义。 如果此 UUID 不是基于时间的 UUID,则此方法抛出
 | 
			
		||||
     * UnsupportedOperationException。
 | 
			
		||||
     *
 | 
			
		||||
     * @return 此 {@code UUID} 的时钟序列
 | 
			
		||||
     *
 | 
			
		||||
     * @throws UnsupportedOperationException 如果此 UUID 的 version 不为 1
 | 
			
		||||
     */
 | 
			
		||||
    public int clockSequence() throws UnsupportedOperationException
 | 
			
		||||
    {
 | 
			
		||||
        checkTimeBase();
 | 
			
		||||
        return (int) ((leastSigBits & 0x3FFF000000000000L) >>> 48);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 与此 UUID 相关的节点值。
 | 
			
		||||
     *
 | 
			
		||||
     * <p>
 | 
			
		||||
     * 48 位的节点值根据此 UUID 的 node 字段构造。此字段旨在用于保存机器的 IEEE 802 地址,该地址用于生成此 UUID 以保证空间唯一性。
 | 
			
		||||
     * <p>
 | 
			
		||||
     * 节点值仅在基于时间的 UUID(其 version 类型为 1)中才有意义。<br>
 | 
			
		||||
     * 如果此 UUID 不是基于时间的 UUID,则此方法抛出 UnsupportedOperationException。
 | 
			
		||||
     *
 | 
			
		||||
     * @return 此 {@code UUID} 的节点值
 | 
			
		||||
     *
 | 
			
		||||
     * @throws UnsupportedOperationException 如果此 UUID 的 version 不为 1
 | 
			
		||||
     */
 | 
			
		||||
    public long node() throws UnsupportedOperationException
 | 
			
		||||
    {
 | 
			
		||||
        checkTimeBase();
 | 
			
		||||
        return leastSigBits & 0x0000FFFFFFFFFFFFL;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 返回此{@code UUID} 的字符串表现形式。
 | 
			
		||||
     *
 | 
			
		||||
     * <p>
 | 
			
		||||
     * UUID 的字符串表示形式由此 BNF 描述:
 | 
			
		||||
     * 
 | 
			
		||||
     * <pre>
 | 
			
		||||
     * {@code
 | 
			
		||||
     * UUID                   = <time_low>-<time_mid>-<time_high_and_version>-<variant_and_sequence>-<node>
 | 
			
		||||
     * time_low               = 4*<hexOctet>
 | 
			
		||||
     * time_mid               = 2*<hexOctet>
 | 
			
		||||
     * time_high_and_version  = 2*<hexOctet>
 | 
			
		||||
     * variant_and_sequence   = 2*<hexOctet>
 | 
			
		||||
     * node                   = 6*<hexOctet>
 | 
			
		||||
     * hexOctet               = <hexDigit><hexDigit>
 | 
			
		||||
     * hexDigit               = [0-9a-fA-F]
 | 
			
		||||
     * }
 | 
			
		||||
     * </pre>
 | 
			
		||||
     * 
 | 
			
		||||
     * </blockquote>
 | 
			
		||||
     *
 | 
			
		||||
     * @return 此{@code UUID} 的字符串表现形式
 | 
			
		||||
     * @see #toString(boolean)
 | 
			
		||||
     */
 | 
			
		||||
    @Override
 | 
			
		||||
    public String toString()
 | 
			
		||||
    {
 | 
			
		||||
        return toString(false);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 返回此{@code UUID} 的字符串表现形式。
 | 
			
		||||
     *
 | 
			
		||||
     * <p>
 | 
			
		||||
     * UUID 的字符串表示形式由此 BNF 描述:
 | 
			
		||||
     * 
 | 
			
		||||
     * <pre>
 | 
			
		||||
     * {@code
 | 
			
		||||
     * UUID                   = <time_low>-<time_mid>-<time_high_and_version>-<variant_and_sequence>-<node>
 | 
			
		||||
     * time_low               = 4*<hexOctet>
 | 
			
		||||
     * time_mid               = 2*<hexOctet>
 | 
			
		||||
     * time_high_and_version  = 2*<hexOctet>
 | 
			
		||||
     * variant_and_sequence   = 2*<hexOctet>
 | 
			
		||||
     * node                   = 6*<hexOctet>
 | 
			
		||||
     * hexOctet               = <hexDigit><hexDigit>
 | 
			
		||||
     * hexDigit               = [0-9a-fA-F]
 | 
			
		||||
     * }
 | 
			
		||||
     * </pre>
 | 
			
		||||
     * 
 | 
			
		||||
     * </blockquote>
 | 
			
		||||
     *
 | 
			
		||||
     * @param isSimple 是否简单模式,简单模式为不带'-'的UUID字符串
 | 
			
		||||
     * @return 此{@code UUID} 的字符串表现形式
 | 
			
		||||
     */
 | 
			
		||||
    public String toString(boolean isSimple)
 | 
			
		||||
    {
 | 
			
		||||
        final StringBuilder builder = new StringBuilder(isSimple ? 32 : 36);
 | 
			
		||||
        // time_low
 | 
			
		||||
        builder.append(digits(mostSigBits >> 32, 8));
 | 
			
		||||
        if (false == isSimple)
 | 
			
		||||
        {
 | 
			
		||||
            builder.append('-');
 | 
			
		||||
        }
 | 
			
		||||
        // time_mid
 | 
			
		||||
        builder.append(digits(mostSigBits >> 16, 4));
 | 
			
		||||
        if (false == isSimple)
 | 
			
		||||
        {
 | 
			
		||||
            builder.append('-');
 | 
			
		||||
        }
 | 
			
		||||
        // time_high_and_version
 | 
			
		||||
        builder.append(digits(mostSigBits, 4));
 | 
			
		||||
        if (false == isSimple)
 | 
			
		||||
        {
 | 
			
		||||
            builder.append('-');
 | 
			
		||||
        }
 | 
			
		||||
        // variant_and_sequence
 | 
			
		||||
        builder.append(digits(leastSigBits >> 48, 4));
 | 
			
		||||
        if (false == isSimple)
 | 
			
		||||
        {
 | 
			
		||||
            builder.append('-');
 | 
			
		||||
        }
 | 
			
		||||
        // node
 | 
			
		||||
        builder.append(digits(leastSigBits, 12));
 | 
			
		||||
 | 
			
		||||
        return builder.toString();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 返回此 UUID 的哈希码。
 | 
			
		||||
     *
 | 
			
		||||
     * @return UUID 的哈希码值。
 | 
			
		||||
     */
 | 
			
		||||
    @Override
 | 
			
		||||
    public int hashCode()
 | 
			
		||||
    {
 | 
			
		||||
        long hilo = mostSigBits ^ leastSigBits;
 | 
			
		||||
        return ((int) (hilo >> 32)) ^ (int) hilo;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 将此对象与指定对象比较。
 | 
			
		||||
     * <p>
 | 
			
		||||
     * 当且仅当参数不为 {@code null}、而是一个 UUID 对象、具有与此 UUID 相同的 varriant、包含相同的值(每一位均相同)时,结果才为 {@code true}。
 | 
			
		||||
     *
 | 
			
		||||
     * @param obj 要与之比较的对象
 | 
			
		||||
     *
 | 
			
		||||
     * @return 如果对象相同,则返回 {@code true};否则返回 {@code false}
 | 
			
		||||
     */
 | 
			
		||||
    @Override
 | 
			
		||||
    public boolean equals(Object obj)
 | 
			
		||||
    {
 | 
			
		||||
        if ((null == obj) || (obj.getClass() != UUID.class))
 | 
			
		||||
        {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
        UUID id = (UUID) obj;
 | 
			
		||||
        return (mostSigBits == id.mostSigBits && leastSigBits == id.leastSigBits);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Comparison Operations
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 将此 UUID 与指定的 UUID 比较。
 | 
			
		||||
     *
 | 
			
		||||
     * <p>
 | 
			
		||||
     * 如果两个 UUID 不同,且第一个 UUID 的最高有效字段大于第二个 UUID 的对应字段,则第一个 UUID 大于第二个 UUID。
 | 
			
		||||
     *
 | 
			
		||||
     * @param val 与此 UUID 比较的 UUID
 | 
			
		||||
     *
 | 
			
		||||
     * @return 在此 UUID 小于、等于或大于 val 时,分别返回 -1、0 或 1。
 | 
			
		||||
     *
 | 
			
		||||
     */
 | 
			
		||||
    @Override
 | 
			
		||||
    public int compareTo(UUID val)
 | 
			
		||||
    {
 | 
			
		||||
        // The ordering is intentionally set up so that the UUIDs
 | 
			
		||||
        // can simply be numerically compared as two numbers
 | 
			
		||||
        return (this.mostSigBits < val.mostSigBits ? -1 : //
 | 
			
		||||
                (this.mostSigBits > val.mostSigBits ? 1 : //
 | 
			
		||||
                        (this.leastSigBits < val.leastSigBits ? -1 : //
 | 
			
		||||
                                (this.leastSigBits > val.leastSigBits ? 1 : //
 | 
			
		||||
                                        0))));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // -------------------------------------------------------------------------------------------------------------------
 | 
			
		||||
    // Private method start
 | 
			
		||||
    /**
 | 
			
		||||
     * 返回指定数字对应的hex值
 | 
			
		||||
     * 
 | 
			
		||||
     * @param val 值
 | 
			
		||||
     * @param digits 位
 | 
			
		||||
     * @return 值
 | 
			
		||||
     */
 | 
			
		||||
    private static String digits(long val, int digits)
 | 
			
		||||
    {
 | 
			
		||||
        long hi = 1L << (digits * 4);
 | 
			
		||||
        return Long.toHexString(hi | (val & (hi - 1))).substring(1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 检查是否为time-based版本UUID
 | 
			
		||||
     */
 | 
			
		||||
    private void checkTimeBase()
 | 
			
		||||
    {
 | 
			
		||||
        if (version() != 1)
 | 
			
		||||
        {
 | 
			
		||||
            throw new UnsupportedOperationException("Not a time-based UUID");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获取{@link SecureRandom},类提供加密的强随机数生成器 (RNG)
 | 
			
		||||
     * 
 | 
			
		||||
     * @return {@link SecureRandom}
 | 
			
		||||
     */
 | 
			
		||||
    public static SecureRandom getSecureRandom()
 | 
			
		||||
    {
 | 
			
		||||
        try
 | 
			
		||||
        {
 | 
			
		||||
            return SecureRandom.getInstance("SHA1PRNG");
 | 
			
		||||
        }
 | 
			
		||||
        catch (NoSuchAlgorithmException e)
 | 
			
		||||
        {
 | 
			
		||||
            throw new UtilException(e);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获取随机数生成器对象<br>
 | 
			
		||||
     * ThreadLocalRandom是JDK 7之后提供并发产生随机数,能够解决多个线程发生的竞争争夺。
 | 
			
		||||
     * 
 | 
			
		||||
     * @return {@link ThreadLocalRandom}
 | 
			
		||||
     */
 | 
			
		||||
    public static ThreadLocalRandom getRandom()
 | 
			
		||||
    {
 | 
			
		||||
        return ThreadLocalRandom.current();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -55,18 +55,6 @@
 | 
			
		||||
            <artifactId>druid-spring-boot-starter</artifactId>
 | 
			
		||||
        </dependency>
 | 
			
		||||
 | 
			
		||||
        <!-- 验证码 -->
 | 
			
		||||
        <dependency>
 | 
			
		||||
            <groupId>com.github.penggle</groupId>
 | 
			
		||||
            <artifactId>kaptcha</artifactId>
 | 
			
		||||
            <exclusions>
 | 
			
		||||
                <exclusion>
 | 
			
		||||
                    <artifactId>javax.servlet-api</artifactId>
 | 
			
		||||
                    <groupId>javax.servlet</groupId>
 | 
			
		||||
                </exclusion>
 | 
			
		||||
            </exclusions>
 | 
			
		||||
        </dependency>
 | 
			
		||||
 | 
			
		||||
        <!-- 获取系统信息 -->
 | 
			
		||||
        <dependency>
 | 
			
		||||
            <groupId>com.github.oshi</groupId>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,83 +1,55 @@
 | 
			
		||||
package com.ruoyi.framework.config;
 | 
			
		||||
 | 
			
		||||
import java.util.Properties;
 | 
			
		||||
import java.awt.*;
 | 
			
		||||
 | 
			
		||||
import cn.hutool.captcha.*;
 | 
			
		||||
import org.springframework.context.annotation.Bean;
 | 
			
		||||
import org.springframework.context.annotation.Configuration;
 | 
			
		||||
import com.google.code.kaptcha.impl.DefaultKaptcha;
 | 
			
		||||
import com.google.code.kaptcha.util.Config;
 | 
			
		||||
import static com.google.code.kaptcha.Constants.*;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 验证码配置
 | 
			
		||||
 * 
 | 
			
		||||
 * @author ruoyi
 | 
			
		||||
 *
 | 
			
		||||
 * @author Lion Li
 | 
			
		||||
 */
 | 
			
		||||
@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;
 | 
			
		||||
public class CaptchaConfig {
 | 
			
		||||
 | 
			
		||||
    private final int width = 160;
 | 
			
		||||
    private final int height = 60;
 | 
			
		||||
    private final Color background = Color.PINK;
 | 
			
		||||
    private final Font font = new Font("Arial", Font.BOLD, 48);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 圆圈干扰验证码
 | 
			
		||||
     */
 | 
			
		||||
    @Bean(name = "CircleCaptcha")
 | 
			
		||||
    public CircleCaptcha getCircleCaptcha() {
 | 
			
		||||
        CircleCaptcha captcha = CaptchaUtil.createCircleCaptcha(width, height);
 | 
			
		||||
        captcha.setBackground(background);
 | 
			
		||||
        captcha.setFont(font);
 | 
			
		||||
        return captcha;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @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.ruoyi.framework.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;
 | 
			
		||||
    /**
 | 
			
		||||
     * 线段干扰的验证码
 | 
			
		||||
     */
 | 
			
		||||
    @Bean(name = "LineCaptcha")
 | 
			
		||||
    public LineCaptcha getLineCaptcha() {
 | 
			
		||||
        LineCaptcha captcha = CaptchaUtil.createLineCaptcha(width, height);
 | 
			
		||||
        captcha.setBackground(background);
 | 
			
		||||
        captcha.setFont(font);
 | 
			
		||||
        return captcha;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 扭曲干扰验证码
 | 
			
		||||
     */
 | 
			
		||||
    @Bean(name = "ShearCaptcha")
 | 
			
		||||
    public ShearCaptcha getShearCaptcha() {
 | 
			
		||||
        ShearCaptcha captcha = CaptchaUtil.createShearCaptcha(width, height);
 | 
			
		||||
        captcha.setBackground(background);
 | 
			
		||||
        captcha.setFont(font);
 | 
			
		||||
        return captcha;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,75 +0,0 @@
 | 
			
		||||
package com.ruoyi.framework.config;
 | 
			
		||||
 | 
			
		||||
import java.util.Random;
 | 
			
		||||
import com.google.code.kaptcha.text.impl.DefaultTextCreator;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 验证码文本生成器
 | 
			
		||||
 * 
 | 
			
		||||
 * @author ruoyi
 | 
			
		||||
 */
 | 
			
		||||
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 = (int) Math.round(Math.random() * 2);
 | 
			
		||||
        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 (randomoperands == 2)
 | 
			
		||||
        {
 | 
			
		||||
            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]);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            result = x + y;
 | 
			
		||||
            suChinese.append(CNUMBERS[x]);
 | 
			
		||||
            suChinese.append("+");
 | 
			
		||||
            suChinese.append(CNUMBERS[y]);
 | 
			
		||||
        }
 | 
			
		||||
        suChinese.append("=?@" + result);
 | 
			
		||||
        return suChinese.toString();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -4,6 +4,8 @@ import java.util.HashMap;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
import java.util.concurrent.TimeUnit;
 | 
			
		||||
import javax.servlet.http.HttpServletRequest;
 | 
			
		||||
 | 
			
		||||
import cn.hutool.core.util.IdUtil;
 | 
			
		||||
import org.springframework.beans.factory.annotation.Autowired;
 | 
			
		||||
import org.springframework.beans.factory.annotation.Value;
 | 
			
		||||
import org.springframework.stereotype.Component;
 | 
			
		||||
@@ -14,7 +16,6 @@ import com.ruoyi.common.utils.ServletUtils;
 | 
			
		||||
import com.ruoyi.common.utils.StringUtils;
 | 
			
		||||
import com.ruoyi.common.utils.ip.AddressUtils;
 | 
			
		||||
import com.ruoyi.common.utils.ip.IpUtils;
 | 
			
		||||
import com.ruoyi.common.utils.uuid.IdUtils;
 | 
			
		||||
import eu.bitwalker.useragentutils.UserAgent;
 | 
			
		||||
import io.jsonwebtoken.Claims;
 | 
			
		||||
import io.jsonwebtoken.Jwts;
 | 
			
		||||
@@ -101,7 +102,7 @@ public class TokenService
 | 
			
		||||
     */
 | 
			
		||||
    public String createToken(LoginUser loginUser)
 | 
			
		||||
    {
 | 
			
		||||
        String token = IdUtils.fastUUID();
 | 
			
		||||
        String token = IdUtil.fastUUID();
 | 
			
		||||
        loginUser.setToken(token);
 | 
			
		||||
        setUserAgent(loginUser);
 | 
			
		||||
        refreshToken(loginUser);
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user