Browse Source

添加license限制功能

Gaokun Wang 3 tháng trước cách đây
mục cha
commit
ec6477a1fa

+ 18 - 0
als-modules/license/pom.xml

@@ -0,0 +1,18 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.eco</groupId>
+        <artifactId>als-modules</artifactId>
+        <version>${revision}</version>
+    </parent>
+
+    <artifactId>license</artifactId>
+    <packaging>jar</packaging>
+    <dependencies>
+        <dependency>
+            <groupId>org.eco</groupId>
+            <artifactId>system</artifactId>
+        </dependency>
+    </dependencies>
+</project>

+ 52 - 0
als-modules/license/src/main/java/org/eco/als/core/LicenseGenerator.java

@@ -0,0 +1,52 @@
+package org.eco.als.core;
+
+
+import java.io.FileOutputStream;
+import java.nio.charset.StandardCharsets;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.PrivateKey;
+import java.security.Signature;
+import java.time.LocalDate;
+import java.util.Base64;
+import java.util.Properties;
+
+/**
+ * @description LicenseGenerator
+ *
+ * @author GaoKunW
+ * @date 2025/3/25 17:04
+ */
+public class LicenseGenerator {
+    public static void main(String[] args) throws Exception {
+        // 生成密钥对(首次运行时生成)
+        KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
+        keyGen.initialize(2048);
+        KeyPair keyPair = keyGen.generateKeyPair();
+
+        // 保存公钥到资源目录
+        try (FileOutputStream fos = new FileOutputStream("publicKey.der")) {
+            fos.write(keyPair.getPublic().getEncoded());
+        }
+
+        // 创建许可证
+        Properties props = new Properties();
+        props.setProperty("mac", SystemInfo.getMacAddress());
+        props.setProperty("host", SystemInfo.getHostName());
+        props.setProperty("expiry", LocalDate.now().plusYears(1).toString());
+
+        // 生成签名
+        PrivateKey privateKey = keyPair.getPrivate();
+        Signature sig = Signature.getInstance("SHA256withRSA");
+        sig.initSign(privateKey);
+        String data = props.getProperty("mac") + props.getProperty("host") + props.getProperty("expiry");
+        sig.update(data.getBytes(StandardCharsets.UTF_8));
+        String signature = Base64.getEncoder().encodeToString(sig.sign());
+        props.setProperty("signature", signature);
+
+        // 保存许可证
+        try (FileOutputStream fos = new FileOutputStream("license.lic")) {
+            props.store(fos, "License File");
+        }
+    }
+}

+ 66 - 0
als-modules/license/src/main/java/org/eco/als/core/LicenseValidator.java

@@ -0,0 +1,66 @@
+package org.eco.als.core;
+
+
+import org.eco.als.exception.LicenseException;
+import org.springframework.core.io.ClassPathResource;
+import org.springframework.stereotype.Service;
+
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.security.KeyFactory;
+import java.security.PublicKey;
+import java.security.Signature;
+import java.security.spec.X509EncodedKeySpec;
+import java.time.LocalDate;
+import java.util.Base64;
+import java.util.Properties;
+
+/**
+ * @author GaoKunW
+ * @description LicenseValidator
+ * @date 2025/3/25 16:36
+ */
+@Service
+public class LicenseValidator {
+    public static void validate() throws Exception {
+        Properties props = new Properties();
+        try (InputStream is = Files.newInputStream(Paths.get("license.lic"))) {
+            props.load(is);
+        }
+
+        String licenseMac = props.getProperty("mac");
+        String licenseHost = props.getProperty("host");
+        String expiryDate = props.getProperty("expiry");
+        String signature = props.getProperty("signature");
+
+        // 验证系统信息
+        if (!licenseMac.equals(SystemInfo.getMacAddress()) ||
+            !licenseHost.equals(SystemInfo.getHostName())) {
+            throw new LicenseException("License does not match current system");
+        }
+
+        // 验证过期时间
+        if (LocalDate.now().isAfter(LocalDate.parse(expiryDate))) {
+            throw new LicenseException("License has expired");
+        }
+
+        // 验证签名
+        PublicKey publicKey = readPublicKey();
+        Signature sig = Signature.getInstance("SHA256withRSA");
+        sig.initVerify(publicKey);
+        sig.update((licenseMac + licenseHost + expiryDate).getBytes());
+
+        if (!sig.verify(Base64.getDecoder().decode(signature))) {
+            throw new LicenseException("Invalid license signature");
+        }
+    }
+
+    private static PublicKey readPublicKey() throws Exception {
+        try (InputStream is = new ClassPathResource("publicKey.der").getInputStream()) {
+            byte[] keyBytes = is.readAllBytes();
+            X509EncodedKeySpec spec = new X509EncodedKeySpec(keyBytes);
+            return KeyFactory.getInstance("RSA").generatePublic(spec);
+        }
+    }
+}

+ 37 - 0
als-modules/license/src/main/java/org/eco/als/core/SystemInfo.java

@@ -0,0 +1,37 @@
+package org.eco.als.core;
+
+
+import java.net.InetAddress;
+import java.net.NetworkInterface;
+import java.util.Enumeration;
+
+/**
+ * @author GaoKunW
+ * @description SystemInfo
+ * @date 2025/3/25 16:36
+ */
+public class SystemInfo {
+    public static String getMacAddress() throws Exception {
+        StringBuilder sb = new StringBuilder();
+        Enumeration<NetworkInterface> networks = NetworkInterface.getNetworkInterfaces();
+
+        while (networks.hasMoreElements()) {
+            NetworkInterface network = networks.nextElement();
+            if (network.isVirtual() || !network.isUp()) {
+                continue;
+            }
+
+            byte[] mac = network.getHardwareAddress();
+            if (mac != null) {
+                for (byte b : mac) {
+                    sb.append(String.format("%02X", b));
+                }
+            }
+        }
+        return !sb.isEmpty() ? sb.toString() : "";
+    }
+
+    public static String getHostName() throws Exception {
+        return InetAddress.getLocalHost().getHostName();
+    }
+}

+ 14 - 0
als-modules/license/src/main/java/org/eco/als/exception/LicenseException.java

@@ -0,0 +1,14 @@
+package org.eco.als.exception;
+
+
+/**
+ * @description LicenseException
+ *
+ * @author GaoKunW
+ * @date 2025/3/25 16:28
+ */
+public class LicenseException extends RuntimeException  {
+    public LicenseException(String message) {
+        super(message);
+    }
+}

BIN
als-modules/license/src/main/resources/publicKey.der


+ 1 - 0
als-modules/pom.xml

@@ -15,6 +15,7 @@
         <module>job</module>
         <module>system</module>
         <module>agile-assurance</module>
+        <module>license</module>
     </modules>
 
     <artifactId>als-modules</artifactId>

+ 6 - 0
als-start/pom.xml

@@ -91,6 +91,12 @@
             <groupId>de.codecentric</groupId>
             <artifactId>spring-boot-admin-starter-client</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.eco</groupId>
+            <artifactId>license</artifactId>
+            <version>1.1.0</version>
+            <scope>compile</scope>
+        </dependency>
 
         <!--  powerjob 客户端  -->
         <!--        <dependency>-->

+ 12 - 0
als-start/src/main/java/org/eco/StartApplication.java

@@ -1,6 +1,9 @@
 package org.eco;
 
+import jakarta.annotation.PostConstruct;
 import org.dromara.easyes.starter.register.EsMapperScan;
+import org.eco.als.core.LicenseValidator;
+import org.eco.als.exception.LicenseException;
 import org.springframework.boot.SpringApplication;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
 import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
@@ -14,6 +17,15 @@ import org.springframework.boot.context.metrics.buffering.BufferingApplicationSt
 @SpringBootApplication(exclude = {DataSourceAutoConfiguration.class}, scanBasePackages = {"org.eco"})
 @EsMapperScan("org.eco.als.es.mapper")
 public class StartApplication {
+    @PostConstruct
+    public void validateLicense() {
+        try {
+            LicenseValidator.validate();
+        } catch (Exception e) {
+            throw new LicenseException("License validation failed: " + e.getMessage());
+        }
+    }
+
     public static void main(String[] args) {
         SpringApplication application = new SpringApplication(StartApplication.class);
         application.setApplicationStartup(new BufferingApplicationStartup(2048));

+ 0 - 1
als-start/src/main/resources/application.yml

@@ -328,4 +328,3 @@ websocket:
   path: /resource/websocket
   # 设置访问源地址
   allowedOrigins: '*'
-

+ 6 - 0
license.lic

@@ -0,0 +1,6 @@
+#License File
+#Tue Mar 25 17:07:30 CST 2025
+expiry=2026-03-25
+host=DESKTOP-EMDT8C1
+mac=00155D15244500155D15244500155D152445344B50000000344B50000000344B5000000000155D5E029100155D5E029100155D5E0291344B5000000000155D15244500155D5E0291F854F6005C99F854F6005C99F854F6005C99F854F6005C99F854F6005C99F854F6005C99
+signature=cS8E6qEqU9t5FuLSE2J0pBFlppTjSOOVPa/RJYlJeaCLX3HKPn5h6pMewHcz/5rXeZAqgtK7dKUNJA8kf3qRXYhPIvJTwovm5K2WG6xCbl8y5H35JNPj5ht4gBiMbyWe/hz0hi0oAFYCoWbPjUpEXnSPgLiuZClpPkCh8FQlGmKMvllXyC1QouEQzApmhM8q3qnbONmjPLkQrkFLzsvA3J1z2Twof7rQUCjlBSkCWqtjE0Vh4+UJe9XhZq7WLk1PBU/YqKejsKwFlZyTpT7En022c/SpAbO24bsbSUcgucgBDangBfaz0gNMce9cSpjd0YhdeQjI0lYd+he7yyz2+A\=\=