Sfoglia il codice sorgente

License机制添加

zhanghaijun 2 anni fa
parent
commit
7c8f3ac2af

+ 6 - 4
train-common/train-common-core/src/main/java/top/haijunit/common/config/LicenseFilter.java

@@ -5,10 +5,13 @@ import jakarta.servlet.*;
 import jakarta.servlet.annotation.WebFilter;
 import lombok.RequiredArgsConstructor;
 import org.springframework.stereotype.Component;
+import top.haijunit.common.domain.LicenseService;
 import top.haijunit.common.domain.ResultData;
+import top.haijunit.common.utils.SpringUtils;
 
 import java.io.IOException;
 import java.io.PrintWriter;
+import java.util.Optional;
 
 /**
  * @author zhanghaijun
@@ -20,15 +23,14 @@ import java.io.PrintWriter;
 @RequiredArgsConstructor
 public class LicenseFilter implements Filter {
 
-    private final LicenseService licenseService;
-
     @Override
     public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
-        if (licenseService.licenseExpired()) {
+        Optional<LicenseService> licenseOptional = Optional.of(SpringUtils.getBean(LicenseService.class));
+        if (licenseOptional.map(LicenseService::licenseExpired).orElse(false)) {
             response.setContentType("application/json");
             response.setCharacterEncoding("UTF-8");
             PrintWriter writer = response.getWriter();
-            writer.write(JSONUtil.toJsonStr(ResultData.fail("插件授权已过期,请联系管理员","")));
+            writer.write(JSONUtil.toJsonStr(ResultData.fail("授权已过期,请联系管理员", "")));
             return;
         }
         chain.doFilter(request, response);

+ 0 - 39
train-common/train-common-core/src/main/java/top/haijunit/common/config/LicenseService.java

@@ -1,39 +0,0 @@
-package top.haijunit.common.config;
-
-import cn.hutool.core.date.DateUtil;
-import lombok.RequiredArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.boot.autoconfigure.AutoConfigureAfter;
-import org.springframework.stereotype.Service;
-
-import java.util.Calendar;
-import java.util.Date;
-import java.util.GregorianCalendar;
-
-/**
- * @author zhanghaijun
- * @date 2023/11/7 12:12
- * @description 授权控制
- */
-@Slf4j
-@Service
-@RequiredArgsConstructor
-@AutoConfigureAfter
-public class LicenseService {
-
-    public Date getLicenseUntil() {
-        return DateUtil.endOfDay(new GregorianCalendar(2024, Calendar.DECEMBER, 31)).getTime();
-    }
-
-    public boolean licenseExpired() {
-        return DateUtil.compare(DateUtil.date(), getLicenseUntil()) > 0;
-    }
-
-    public String getLicenseUntilStr() {
-        return DateUtil.formatDateTime(getLicenseUntil());
-    }
-
-    public long licenseRemaining() {
-        return DateUtil.betweenDay(getLicenseUntil(), DateUtil.date(), true);
-    }
-}

+ 28 - 26
train-common/train-common-core/src/main/java/top/haijunit/common/config/RunnerLog.java

@@ -10,7 +10,9 @@ import org.springframework.boot.ApplicationRunner;
 import org.springframework.boot.SpringApplication;
 import org.springframework.context.ApplicationContext;
 import org.springframework.stereotype.Component;
+import top.haijunit.common.domain.LicenseService;
 import top.haijunit.common.utils.LicenseUtil;
+import top.haijunit.common.utils.SpringUtils;
 
 import java.lang.management.ManagementFactory;
 import java.util.Date;
@@ -26,17 +28,19 @@ import java.util.Optional;
 @RequiredArgsConstructor
 public class RunnerLog implements ApplicationRunner {
     private final ApplicationContext ctx;
-    private final LicenseService licenseService;
 
     @Override
     public void run(ApplicationArguments args) {
-        if (licenseService.licenseExpired()) {
+        Optional<LicenseService> licenseOptional = Optional.of(SpringUtils.getBean(LicenseService.class));
+        if (licenseOptional.map(LicenseService::licenseExpired).orElse(false)) {
             LicenseUtil.runVbsExpired();
-            log.info("\033[1;33m {} \033[0m", String.format("\n ========== 启动服务 =========="
-                + "\n\t 授权时间已过期,请来及时联系管理员"
-                + "\n\t 授权时间至:\t%s"
-                + "\n\t 授权已过期:\t%s天"
-                + "\n ============================", licenseService.getLicenseUntilStr(), licenseService.licenseRemaining()));
+            log.info("\033[1;33m {} \033[0m", String.format("""
+
+                 ========== 启动服务 ==========
+                \t 授权时间已过期,请来及时联系管理员
+                \t 授权时间至:\t%s
+                \t 授权已过期:\t%s天
+                 ============================""", licenseOptional.map(LicenseService::getLicenseUntilStr).orElse(""), licenseOptional.map(LicenseService::licenseRemaining).orElse(0L)));
             System.exit(SpringApplication.exit(ctx));
         }
         String appJvmName = ManagementFactory.getRuntimeMXBean().getName();
@@ -44,30 +48,28 @@ public class RunnerLog implements ApplicationRunner {
         String httpPort = ctx.getEnvironment().getProperty("server.port");
         String httpHost = ctx.getEnvironment().getProperty("project.plugin.ip-address");
         String httpUrl = "http://" + Optional.ofNullable(httpHost).orElse("127.0.0.1") + ":" + Optional.ofNullable(httpPort).orElse("8080") + path;
-        String format = String.format("\n ========== 启动服务 =========="
-                + "\n\t 服务名称:\t%s"
-                + "\n\t 授权时间至:\t%s"
-                + "\n\t 授权剩余:\t%s天"
-                + "\n\t 进程ID: \t%s"
-                + "\n\t 启动时间:\t%s"
-                + "\n\t 访问路径:\t%s"
-                + "\n ============================",
-            ctx.getEnvironment().getProperty("spring.application.name"),
-            licenseService.getLicenseUntilStr(), licenseService.licenseRemaining(),
-            appJvmName.split("@")[0],
-            DateUtil.format(new Date(ctx.getStartupDate()), DatePattern.NORM_DATETIME_MS_PATTERN),
-            httpUrl
-        );
+        String format = String.format("""
+
+             ========== 启动服务 ==========
+            \t 服务名称:\t%s
+            \t 授权时间至:\t%s
+            \t 授权剩余:\t%s天
+            \t 进程ID: \t%s
+            \t 启动时间:\t%s
+            \t 访问路径:\t%s
+             ============================""", ctx.getEnvironment().getProperty("spring.application.name"), licenseOptional.map(LicenseService::getLicenseUntilStr).orElse(""), licenseOptional.map(LicenseService::licenseRemaining).orElse(0L), appJvmName.split("@")[0], DateUtil.format(new Date(ctx.getStartupDate()), DatePattern.NORM_DATETIME_MS_PATTERN), httpUrl);
         log.info("\033[1;33m {} \033[0m", format);
     }
 
     @PreDestroy
     public void destroy() {
-        String format = String.format("\n ========== 暂停服务 =========="
-                + "\n\t 服务名称:\t%s"
-                + "\n\t 暂停时间:\t%s"
-                + "\n============================",
-            ctx.getEnvironment().getProperty("spring.application.name"), DateUtil.now());
+        String format = String.format("""
+
+             ========== 暂停服务 ==========
+            \t 服务名称:\t%s
+            \t 暂停时间:\t%s
+            ============================
+            """, ctx.getEnvironment().getProperty("spring.application.name"), DateUtil.now());
         log.info("\033[1;33m {} \033[0m", format);
     }
 }

+ 18 - 0
train-common/train-common-core/src/main/java/top/haijunit/common/domain/LicenseItem.java

@@ -0,0 +1,18 @@
+package top.haijunit.common.domain;
+
+import lombok.Data;
+import lombok.RequiredArgsConstructor;
+
+/**
+ * @author zhanghaijun
+ * @date 2023/12/21 15:14
+ * @description 授权信息
+ */
+@Data
+@RequiredArgsConstructor
+public class LicenseItem {
+
+    private final String machineCode;
+    private final Long time;
+
+}

+ 32 - 0
train-common/train-common-core/src/main/java/top/haijunit/common/domain/LicenseService.java

@@ -0,0 +1,32 @@
+package top.haijunit.common.domain;
+
+import cn.hutool.core.date.DateUtil;
+
+import java.util.Date;
+import java.util.Optional;
+
+/**
+ * @author zhanghaijun
+ * @date 2023/12/21 14:56
+ * @description 授权服务
+ */
+public interface LicenseService {
+
+    LicenseItem getLicenseItem();
+
+    default Date getLicenseUntil() {
+        return Optional.of(this.getLicenseItem()).map(item -> new Date(item.getTime())).orElse(new Date());
+    }
+
+    default boolean licenseExpired() {
+        return DateUtil.compare(DateUtil.date(), getLicenseUntil()) > 0;
+    }
+
+    default String getLicenseUntilStr() {
+        return DateUtil.formatDateTime(getLicenseUntil());
+    }
+
+    default long licenseRemaining() {
+        return DateUtil.betweenDay(getLicenseUntil(), DateUtil.date(), true);
+    }
+}

+ 0 - 1
train-common/train-common-core/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

@@ -1,4 +1,3 @@
-top.haijunit.common.config.LicenseService
 top.haijunit.common.config.ApplicationConfig
 top.haijunit.common.config.AsyncConfig
 top.haijunit.common.config.ExceptionAdvice

+ 2 - 2
train-server/src/main/java/top/haijunit/train/controller/LicenseController.java

@@ -4,7 +4,7 @@ import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.web.bind.annotation.*;
 import top.haijunit.common.domain.ResultData;
-import top.haijunit.train.service.LicenseService;
+import top.haijunit.train.service.LicenseServiceImpl;
 
 /**
  * @author zhanghaijun
@@ -17,7 +17,7 @@ import top.haijunit.train.service.LicenseService;
 @RequestMapping("/license")
 public class LicenseController {
 
-    private final LicenseService licenseService;
+    private final LicenseServiceImpl licenseService;
 
     @GetMapping("/code")
     public ResultData<String> getMachineCode() {

+ 31 - 1
train-server/src/main/java/top/haijunit/train/service/LicenseService.java

@@ -1,13 +1,20 @@
 package top.haijunit.train.service;
 
+import cn.hutool.core.convert.Convert;
+import cn.hutool.core.date.DateUtil;
+import cn.hutool.core.io.FileUtil;
 import cn.hutool.core.util.StrUtil;
 import lombok.extern.slf4j.Slf4j;
+import org.springframework.cache.annotation.CacheEvict;
 import org.springframework.cache.annotation.Cacheable;
 import org.springframework.stereotype.Service;
+import top.haijunit.common.domain.LicenseItem;
+import top.haijunit.common.domain.LicenseService;
 import top.haijunit.common.domain.ResultData;
 import top.haijunit.train.domain.constant.CacheNames;
 import top.haijunit.train.utils.LicenseUtil;
 
+import java.io.File;
 import java.util.List;
 
 /**
@@ -17,7 +24,7 @@ import java.util.List;
  */
 @Slf4j
 @Service
-public class LicenseService {
+public class LicenseServiceImpl implements LicenseService {
 
 
     /**
@@ -35,7 +42,27 @@ public class LicenseService {
         return LicenseUtil.getMachineCode();
     }
 
+
+    @Override
+    @Cacheable(value = CacheNames.LICENSE, key = "'LicenseTime'")
+    public LicenseItem getLicenseItem() {
+        File file = new File(LicenseUtil.LICENSE_FILE);
+        if (!file.exists()) {
+            file = FileUtil.file("classpath:" + LicenseUtil.LICENSE_FILE);
+        }
+        if (file.exists()) {
+            String licenseStr = FileUtil.readUtf8String(file);
+            String string = LicenseUtil.licenseDecrypt(StrUtil.trim(licenseStr));
+            String machine = StrUtil.subBefore(string, LicenseUtil.SEPARATOR, false);
+            if (StrUtil.equalsAnyIgnoreCase(machine, this.getMachineCode())) {
+                return new LicenseItem(machine, Convert.toLong(StrUtil.subAfter(string, LicenseUtil.SEPARATOR, false), 0L));
+            }
+        }
+        return new LicenseItem(this.getMachineCode(), DateUtil.current());
+    }
+
     // 校验授权码
+    @CacheEvict(value = CacheNames.LICENSE, key = "'LicenseTime'")
     public ResultData<String> licenseVerify(String licenseStr, String machineStr) {
         if (machineStr.contains(LicenseUtil.MARK)) {
             String machineCode = StrUtil.subBefore(machineStr, LicenseUtil.MARK, false);
@@ -55,6 +82,9 @@ public class LicenseService {
         if (Long.parseLong(list.get(1)) < System.currentTimeMillis()) {
             return ResultData.fail("授权码已过期", machineStr);
         }
+        File file = FileUtil.writeUtf8String(licenseStr, new File(LicenseUtil.LICENSE_FILE));
+        log.info("授权码写入文件:{}", file.getAbsoluteFile());
         return ResultData.success(machineStr);
     }
+
 }

+ 12 - 12
train-server/src/main/java/top/haijunit/train/utils/LicenseUtil.java

@@ -1,38 +1,32 @@
 package top.haijunit.train.utils;
 
 import cn.hutool.core.util.CharsetUtil;
-import cn.hutool.core.util.IdUtil;
 import cn.hutool.core.util.StrUtil;
 import cn.hutool.crypto.SecureUtil;
 import cn.hutool.crypto.symmetric.AES;
-import cn.hutool.crypto.symmetric.SymmetricAlgorithm;
-import cn.hutool.crypto.symmetric.SymmetricCrypto;
 import lombok.experimental.UtilityClass;
+import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.lang3.StringUtils;
-import org.hibernate.annotations.Cache;
-import org.springframework.cache.annotation.Cacheable;
 import org.springframework.util.DigestUtils;
 import oshi.SystemInfo;
 import oshi.hardware.CentralProcessor;
 import oshi.hardware.HardwareAbstractionLayer;
 
-import java.net.InetAddress;
-import java.net.NetworkInterface;
-import java.net.SocketException;
-import java.net.UnknownHostException;
 import java.nio.charset.StandardCharsets;
-import java.util.Locale;
 
 /**
  * @author zhanghaijun
  * @date 2023/12/19 21:40
  * @description 授权工具类
  */
+@Slf4j
 @UtilityClass
 public class LicenseUtil {
 
     private static final byte[] key = "1234567890123456".getBytes();
     public static final String MARK = "+";
+    public static final String SEPARATOR = ",";
+    public static final String LICENSE_FILE = "license.lic";
 
     /**
      * license加密
@@ -53,8 +47,14 @@ public class LicenseUtil {
      * @return 明文
      */
     public String licenseDecrypt(String licenseStr) {
-        AES aes = SecureUtil.aes(key);
-        return aes.decryptStr(licenseStr, CharsetUtil.CHARSET_UTF_8);
+        try {
+            AES aes = SecureUtil.aes(key);
+            return aes.decryptStr(licenseStr, CharsetUtil.CHARSET_UTF_8);
+        } catch (Exception exception) {
+            log.error("license解密异常: {}", exception.getMessage());
+            return "";
+        }
+
     }
 
     public String getMachineCode() {

+ 1 - 0
train-server/src/main/resources/license.lic

@@ -0,0 +1 @@
+51fb66146f102603cd8118bf4051f50e771d2f3b65fda1ed3010d54525461b3e0e4006842a410c965d19aa1711728695

+ 26 - 20
train-server/src/test/java/top/haijunit/train/test/LicenseTest.java

@@ -5,12 +5,15 @@ import java.util.Arrays;
 import java.util.Date;
 import java.util.List;
 
-import cn.hutool.core.date.DateUnit;
+import cn.hutool.core.date.DatePattern;
 import cn.hutool.core.date.DateUtil;
 import cn.hutool.core.util.StrUtil;
 import lombok.extern.slf4j.Slf4j;
 import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Disabled;
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.CsvSource;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.boot.test.context.SpringBootTest;
 import oshi.SystemInfo;
@@ -38,7 +41,7 @@ import oshi.util.Util;
 import org.junit.jupiter.api.DisplayName;
 import top.haijunit.common.domain.ResultData;
 import top.haijunit.train.TrainServerMain;
-import top.haijunit.train.service.LicenseService;
+import top.haijunit.train.service.LicenseServiceImpl;
 import top.haijunit.train.utils.LicenseUtil;
 
 /**
@@ -52,41 +55,44 @@ import top.haijunit.train.utils.LicenseUtil;
 public class LicenseTest {
 
     @Autowired
-    private LicenseService licenseService;
+    private LicenseServiceImpl licenseService;
 
-    @DisplayName("授权+验证")
+    @DisplayName("获取本机的机器码")
     @Test
-    void licenseValid() {
-        String machineStr = licenseService.getMachineStr();
-        String dateStr = "2024-01-01";
-        String licenseStr = getLicenseStr(machineStr, dateStr);
-        log.info(">>>>>>授权码: 【{}】", licenseStr);
-        ResultData<String> result = licenseService.licenseVerify(licenseStr, machineStr);
-        log.info(">>>>>>验证结果: {}", result);
+    void getMachineStr() {
+        log.info(">>>>>>本机的机器码: {}", this.licenseService.getMachineStr());
     }
 
-    private String getLicenseStr(String machineStr, String dateStr) {
+    @DisplayName("生成授权")
+    @ParameterizedTest
+    @CsvSource({"b6f35e941ae79841494fe2e3e7f961a9+876,2024-01-01", "b6f35e941ae79841494fe2e3e7f961a9+876,2024-12-31"})
+    void generateLicense(String machineStr, String dateStr) {
         if (machineStr.contains(LicenseUtil.MARK)) {
             String machineCode = StrUtil.subBefore(machineStr, LicenseUtil.MARK, false);
             String code = StrUtil.subAfter(machineStr, LicenseUtil.MARK, false);
             Assertions.assertEquals(code, LicenseUtil.generateCheckKey(machineCode));
             machineStr = machineCode;
         }
-        log.info(">>>>>>机器码: {}", machineStr);
-        String string = machineStr + "," + DateUtil.parse(dateStr).getTime();
+        String string = machineStr + LicenseUtil.SEPARATOR + DateUtil.parse(dateStr).getTime();
         String licenseStr = LicenseUtil.licenseEncrypt(string);
         log.info(">>>>>>授权码: {}", licenseStr);
-        return licenseStr;
     }
 
-
-    @Test
-    void getMachineCode() {
-        log.info("机器码: {}", LicenseUtil.getMachineCode());
-        log.info("校验位: {}", LicenseUtil.generateCheckKey(LicenseUtil.getMachineCode()));
+    @DisplayName("更新授权")
+    @ParameterizedTest
+    @CsvSource({"51fb66146f102603cd8118bf4051f50e771d2f3b65fda1ed3010d54525461b3e0e4006842a410c965d19aa1711728695", "51fb66146f102603cd8118bf4051f50e771d2f3b65fda1ed3010d54525461b3edab16ab28983625a494b5831d8d135ec"})
+    void licenseValid(String licenseStr) {
+        String machineStr = licenseService.getMachineStr();
+        ResultData<String> result = licenseService.licenseVerify(licenseStr, machineStr);
+        if (result.codeSucceed()) {
+            log.info("授权更新成功,时间到:{}", DateUtil.format(new Date(this.licenseService.getLicenseItem().getTime()), DatePattern.UTC_SIMPLE_FORMAT));
+        } else {
+            log.info("授权更新失败: {}", result.getMessage());
+        }
     }
 
     @Test
+    @Disabled
     void test() {
         System.out.println("Initializing System...");
         SystemInfo si = new SystemInfo();