From f19762bc440161c40d4e17c6951c073b3fbd2e1d Mon Sep 17 00:00:00 2001
From: pang.jiabao <pang_jiabao@163.com>
Date: 星期三, 03 九月 2025 16:04:19 +0800
Subject: [PATCH] 许可证到期提醒,系统30分钟无操作下线账号

---
 src/main/java/com/zy/system/controller/LicenseCreatorController.java  |  105 ++++
 src/main/java/com/zy/system/entity/license/LicenseVerifyParam.java    |   48 +
 src/main/java/com/zy/system/entity/license/LicenseCheckListener.java  |  122 ++++
 src/main/java/com/zy/system/entity/license/LicenseManagerHolder.java  |   22 
 src/main/java/com/zy/system/entity/LicenseInfos.java                  |   57 ++
 src/main/java/com/zy/system/entity/license/CustomLicenseManager.java  |  241 +++++++++
 src/main/resources/publicCerts.keystore                               |    0 
 src/main/java/com/zy/system/entity/license/LicenseVerify.java         |  126 ++++
 src/main/resources/mapper/LicenseInfosMapper.xml                      |   18 
 src/main/java/com/zy/system/mapper/LicenseInfosMapper.java            |   14 
 src/main/java/com/zy/system/entity/license/LicenseCreator.java        |   86 +++
 src/main/java/com/zy/system/entity/license/LicenseCheck.java          |   45 +
 src/main/java/com/zy/system/entity/license/LinuxServerInfos.java      |   91 +++
 pom.xml                                                               |    5 
 src/main/java/com/zy/common/CodeRes.java                              |    2 
 src/main/java/com/zy/system/entity/license/CustomKeyStoreParam.java   |   59 ++
 src/main/java/com/zy/system/entity/license/LicenseCreatorParam.java   |   96 +++
 src/main/java/com/zy/common/web/AuthController.java                   |    7 
 src/main/java/com/zy/system/service/LicenseInfosService.java          |   10 
 src/main/java/com/zy/system/entity/license/WindowsServerInfos.java    |   85 +++
 src/main/java/com/zy/system/entity/license/AbstractServerInfos.java   |  111 ++++
 src/main/java/com/zy/system/timer/LicenseTimer.java                   |  162 ++++++
 src/main/resources/application.yml                                    |    8 
 src/main/java/com/zy/system/service/impl/LicenseInfosServiceImpl.java |   16 
 24 files changed, 1,536 insertions(+), 0 deletions(-)

diff --git a/pom.xml b/pom.xml
index aced17e..e92480a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -103,6 +103,11 @@
             <artifactId>okhttp</artifactId>
             <version>3.10.0</version>
         </dependency>
+        <dependency>
+            <groupId>de.schlichtherle.truelicense</groupId>
+            <artifactId>truelicense-core</artifactId>
+            <version>1.33</version>
+        </dependency>
     </dependencies>
 
     <build>
diff --git a/src/main/java/com/zy/common/CodeRes.java b/src/main/java/com/zy/common/CodeRes.java
index fd80701..a9ea495 100644
--- a/src/main/java/com/zy/common/CodeRes.java
+++ b/src/main/java/com/zy/common/CodeRes.java
@@ -9,6 +9,8 @@
     String USER_10002 = "10002-璐﹀彿宸茶绂佺敤";
     String USER_10003 = "10003-瀵嗙爜閿欒";
 
+    String SYSTEM_20001 = "20001-璁稿彲璇佸凡澶辨晥";
+
     String EXIST_500 = "500-宸ヤ綔妗�/搴撳瓨鏉$爜鏁版嵁宸插瓨鍦�";
     String PICK_600 = "600-鎷f枡浠诲姟";
     String NO_COMB_700 = "700-璇峰厛缁勬墭";
diff --git a/src/main/java/com/zy/common/web/AuthController.java b/src/main/java/com/zy/common/web/AuthController.java
index bb270cf..c3c9b42 100644
--- a/src/main/java/com/zy/common/web/AuthController.java
+++ b/src/main/java/com/zy/common/web/AuthController.java
@@ -15,6 +15,7 @@
 import com.zy.common.utils.RandomValidateCodeUtil;
 import com.zy.system.entity.*;
 import com.zy.system.service.*;
+import com.zy.system.timer.LicenseTimer;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.transaction.annotation.Transactional;
@@ -45,10 +46,16 @@
     private PermissionService permissionService;
     @Autowired
     private RolePermissionService rolePermissionService;
+    @Autowired
+    private LicenseTimer licenseTimer;
 
     @RequestMapping("/login.action")
     @ManagerAuth(value = ManagerAuth.Auth.NONE, memo = "鐧诲綍")
     public R loginAction(String username, String password){
+        //楠岃瘉璁稿彲璇佹槸鍚︽湁鏁�
+        if (!licenseTimer.getSystemSupport()){
+            return R.parse(CodeRes.SYSTEM_20001);
+        }
         if (username.equals("super") && password.equals(Cools.md5(superPwd))) {
             Map<String, Object> res = new HashMap<>();
             res.put("username", username);
diff --git a/src/main/java/com/zy/system/controller/LicenseCreatorController.java b/src/main/java/com/zy/system/controller/LicenseCreatorController.java
new file mode 100644
index 0000000..13e2b1c
--- /dev/null
+++ b/src/main/java/com/zy/system/controller/LicenseCreatorController.java
@@ -0,0 +1,105 @@
+package com.zy.system.controller;
+
+import com.core.common.Cools;
+import com.core.common.R;
+import com.zy.system.entity.license.*;
+import com.zy.system.timer.LicenseTimer;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.http.MediaType;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.File;
+import java.io.IOException;
+
+/**
+ *
+ * 鐢ㄤ簬鐢熸垚璇佷功鏂囦欢锛屼笉鑳芥斁鍦ㄧ粰瀹㈡埛閮ㄧ讲鐨勪唬鐮侀噷
+ */
+@RestController
+@RequestMapping("/license")
+public class LicenseCreatorController {
+
+    @Value("${license.licensePath}")
+    private String licensePath;
+    @Autowired
+    private LicenseCheckListener licenseCheckListener;
+    @Autowired
+    private LicenseTimer licenseTimer;
+    /**
+     * 鑾峰彇鏈嶅姟鍣ㄧ‖浠朵俊鎭�
+     * @param osName 鎿嶄綔绯荤粺绫诲瀷锛屽鏋滀负绌哄垯鑷姩鍒ゆ柇
+     */
+    @RequestMapping(value = "/getServerInfos",produces = {MediaType.APPLICATION_JSON_UTF8_VALUE})
+    public LicenseCheck getServerInfos(@RequestParam(value = "osName",required = false) String osName) {
+        //鎿嶄綔绯荤粺绫诲瀷
+        if(Cools.isEmpty(osName)){
+            osName = System.getProperty("os.name");
+        }
+        osName = osName.toLowerCase();
+
+        AbstractServerInfos abstractServerInfos = null;
+
+        //鏍规嵁涓嶅悓鎿嶄綔绯荤粺绫诲瀷閫夋嫨涓嶅悓鐨勬暟鎹幏鍙栨柟娉�
+        if (osName.startsWith("windows")) {
+            abstractServerInfos = new WindowsServerInfos();
+        } else if (osName.startsWith("linux")) {
+            abstractServerInfos = new LinuxServerInfos();
+        }else{//鍏朵粬鏈嶅姟鍣ㄧ被鍨�
+            abstractServerInfos = new WindowsServerInfos();
+        }
+
+        return abstractServerInfos.getServerInfos();
+    }
+
+    /**
+     * 鑾峰彇璁稿彲璇佹湁鏁堟湡澶╂暟
+     */
+    @RequestMapping(value = "/getLicenseDays")
+    public R getLicenseDays() {
+        return R.ok(licenseTimer.getLicenseDays());
+    }
+
+    @RequestMapping(value = "/updateLicense")
+    public R updateLicense(@RequestParam("file") MultipartFile[] files){
+        MultipartFile file = files[0];
+
+        String licensePathFileName = this.getClass().getClassLoader().getResource(licensePath).getPath();
+        File licensePathFile = new File(licensePathFileName);
+        //鏈嶅姟鍣ㄧ淇濆瓨鐨勬枃浠跺璞�
+        File serverFile = new File(licensePathFile.getPath());
+        if (serverFile.exists()) {
+            try {
+                serverFile.delete();//瀛樺湪鏂囦欢锛屽垹闄�
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        }
+
+        try {
+            //鍒涘缓鏂囦欢
+            serverFile.createNewFile();
+            //灏嗕笂浼犵殑鏂囦欢鍐欏叆鍒版湇鍔″櫒绔枃浠跺唴
+            file.transferTo(serverFile);
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+
+        //閲嶆柊鍔犺浇璁稿彲璇�
+        boolean loadedLicense = licenseCheckListener.loadLicense();
+        if (loadedLicense) {
+            return R.ok();
+        }
+        return R.error("璁稿彲璇佹洿鏂板け璐�");
+    }
+
+    @RequestMapping(value = "/activate")
+    public R activate() {
+        licenseTimer.timer();
+        return R.ok();
+    }
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/zy/system/entity/LicenseInfos.java b/src/main/java/com/zy/system/entity/LicenseInfos.java
new file mode 100644
index 0000000..891ed32
--- /dev/null
+++ b/src/main/java/com/zy/system/entity/LicenseInfos.java
@@ -0,0 +1,57 @@
+package com.zy.system.entity;
+
+import com.baomidou.mybatisplus.annotations.TableField;
+import com.baomidou.mybatisplus.annotations.TableId;
+import com.baomidou.mybatisplus.annotations.TableName;
+import com.baomidou.mybatisplus.enums.IdType;
+import com.core.common.Cools;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.io.Serializable;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+@Data
+@TableName("sys_license_infos")
+public class LicenseInfos implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    @ApiModelProperty(value= "")
+    @TableId(value = "id", type = IdType.AUTO)
+    private Integer id;
+
+    @ApiModelProperty(value= "")
+    private String license;
+
+    @ApiModelProperty(value= "")
+    private String licenseTime;
+
+    @ApiModelProperty(value= "")
+    @TableField("create_time")
+    @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
+    private Date createTime;
+
+    public LicenseInfos() {}
+
+    public LicenseInfos(String license, Date createTime) {
+        this.license = license;
+        this.createTime = createTime;
+    }
+
+//    LicenseInfos licenseInfos = new LicenseInfos(
+//            null,    // 
+//            null    // 
+//    );
+
+    public String getCreateTime$(){
+        if (Cools.isEmpty(this.createTime)){
+            return "";
+        }
+        return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(this.createTime);
+    }
+
+
+}
diff --git a/src/main/java/com/zy/system/entity/license/AbstractServerInfos.java b/src/main/java/com/zy/system/entity/license/AbstractServerInfos.java
new file mode 100644
index 0000000..cb413ae
--- /dev/null
+++ b/src/main/java/com/zy/system/entity/license/AbstractServerInfos.java
@@ -0,0 +1,111 @@
+package com.zy.system.entity.license;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import java.net.InetAddress;
+import java.net.NetworkInterface;
+import java.net.SocketException;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
+
+/**
+ * 鐢ㄤ簬鑾峰彇瀹㈡埛鏈嶅姟鍣ㄧ殑鍩烘湰淇℃伅锛屽锛欼P銆丮ac鍦板潃銆丆PU搴忓垪鍙枫�佷富鏉垮簭鍒楀彿绛�
+ */
+public abstract class AbstractServerInfos {
+    private static Logger logger = LogManager.getLogger(AbstractServerInfos.class);
+
+    /**
+     * 缁勮闇�瑕侀澶栨牎楠岀殑License鍙傛暟
+     */
+    public LicenseCheck getServerInfos(){
+        LicenseCheck result = new LicenseCheck();
+
+        try {
+//            result.setIpAddress(this.getIpAddress());
+            result.setMacAddress(this.getMacAddress());
+            result.setCpuSerial(this.getCPUSerial());
+            result.setMainBoardSerial(this.getMainBoardSerial());
+        }catch (Exception e){
+            logger.error("鑾峰彇鏈嶅姟鍣ㄧ‖浠朵俊鎭け璐�",e);
+        }
+
+        return result;
+    }
+
+    /**
+     * 鑾峰彇IP鍦板潃
+     */
+    protected abstract List<String> getIpAddress() throws Exception;
+
+    /**
+     * 鑾峰彇Mac鍦板潃
+     */
+    protected abstract List<String> getMacAddress() throws Exception;
+
+    /**
+     * 鑾峰彇CPU搴忓垪鍙�
+     */
+    protected abstract String getCPUSerial() throws Exception;
+
+    /**
+     * 鑾峰彇涓绘澘搴忓垪鍙�
+     */
+    protected abstract String getMainBoardSerial() throws Exception;
+
+    /**
+     * 鑾峰彇褰撳墠鏈嶅姟鍣ㄦ墍鏈夌鍚堟潯浠剁殑InetAddress
+     */
+    protected List<InetAddress> getLocalAllInetAddress() throws Exception {
+        List<InetAddress> result = new ArrayList<>(4);
+
+        // 閬嶅巻鎵�鏈夌殑缃戠粶鎺ュ彛
+        for (Enumeration networkInterfaces = NetworkInterface.getNetworkInterfaces(); networkInterfaces.hasMoreElements(); ) {
+            NetworkInterface iface = (NetworkInterface) networkInterfaces.nextElement();
+            // 鍦ㄦ墍鏈夌殑鎺ュ彛涓嬪啀閬嶅巻IP
+            for (Enumeration inetAddresses = iface.getInetAddresses(); inetAddresses.hasMoreElements(); ) {
+                InetAddress inetAddr = (InetAddress) inetAddresses.nextElement();
+
+                //鎺掗櫎LoopbackAddress銆丼iteLocalAddress銆丩inkLocalAddress銆丮ulticastAddress绫诲瀷鐨処P鍦板潃
+                if(!inetAddr.isLoopbackAddress() /*&& !inetAddr.isSiteLocalAddress()*/
+                        && !inetAddr.isLinkLocalAddress() && !inetAddr.isMulticastAddress()){
+                    result.add(inetAddr);
+                }
+            }
+        }
+
+        return result;
+    }
+
+    /**
+     * 鑾峰彇鏌愪釜缃戠粶鎺ュ彛鐨凪ac鍦板潃
+     */
+    protected String getMacByInetAddress(InetAddress inetAddr){
+        try {
+            byte[] mac = NetworkInterface.getByInetAddress(inetAddr).getHardwareAddress();
+            StringBuffer stringBuffer = new StringBuffer();
+
+            for(int i=0;i<mac.length;i++){
+                if(i != 0) {
+                    stringBuffer.append("-");
+                }
+
+                //灏嗗崄鍏繘鍒禸yte杞寲涓哄瓧绗︿覆
+                String temp = Integer.toHexString(mac[i] & 0xff);
+                if(temp.length() == 1){
+                    stringBuffer.append("0" + temp);
+                }else{
+                    stringBuffer.append(temp);
+                }
+            }
+
+            return stringBuffer.toString().toUpperCase();
+        } catch (SocketException e) {
+            e.printStackTrace();
+        }
+
+        return null;
+    }
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/zy/system/entity/license/CustomKeyStoreParam.java b/src/main/java/com/zy/system/entity/license/CustomKeyStoreParam.java
new file mode 100644
index 0000000..f1cab8b
--- /dev/null
+++ b/src/main/java/com/zy/system/entity/license/CustomKeyStoreParam.java
@@ -0,0 +1,59 @@
+package com.zy.system.entity.license;
+
+import de.schlichtherle.license.AbstractKeyStoreParam;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * 鑷畾涔塊eyStoreParam锛岀敤浜庡皢鍏閽ュ瓨鍌ㄦ枃浠跺瓨鏀惧埌鍏朵粬纾佺洏浣嶇疆鑰屼笉鏄」鐩腑
+ */
+public class CustomKeyStoreParam extends AbstractKeyStoreParam {
+
+    /**
+     * 鍏挜/绉侀挜鍦ㄧ鐩樹笂鐨勫瓨鍌ㄨ矾寰�
+     */
+    private String storePath;
+    private String alias;
+    private String storePwd;
+    private String keyPwd;
+
+    public CustomKeyStoreParam(Class clazz, String resource, String alias, String storePwd, String keyPwd) {
+        super(clazz, resource);
+        this.storePath = resource;
+        this.alias = alias;
+        this.storePwd = storePwd;
+        this.keyPwd = keyPwd;
+    }
+
+
+    @Override
+    public String getAlias() {
+        return alias;
+    }
+
+    @Override
+    public String getStorePwd() {
+        return storePwd;
+    }
+
+    @Override
+    public String getKeyPwd() {
+        return keyPwd;
+    }
+
+    /**
+     * 澶嶅啓de.schlichtherle.license.AbstractKeyStoreParam鐨刧etStream()鏂规硶<br/>
+     * 鐢ㄤ簬灏嗗叕绉侀挜瀛樺偍鏂囦欢瀛樻斁鍒板叾浠栫鐩樹綅缃�屼笉鏄」鐩腑
+     */
+    @Override
+    public InputStream getStream() throws IOException {
+        final InputStream in = this.getClass().getClassLoader().getResourceAsStream(storePath);
+        if (null == in) {
+            throw new FileNotFoundException(storePath);
+        }
+
+        return in;
+    }
+}
diff --git a/src/main/java/com/zy/system/entity/license/CustomLicenseManager.java b/src/main/java/com/zy/system/entity/license/CustomLicenseManager.java
new file mode 100644
index 0000000..9a7fc6d
--- /dev/null
+++ b/src/main/java/com/zy/system/entity/license/CustomLicenseManager.java
@@ -0,0 +1,241 @@
+package com.zy.system.entity.license;
+
+import com.core.common.Cools;
+import de.schlichtherle.license.*;
+import de.schlichtherle.xml.GenericCertificate;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import java.beans.XMLDecoder;
+import java.io.BufferedInputStream;
+import java.io.ByteArrayInputStream;
+import java.io.UnsupportedEncodingException;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 鑷畾涔塋icenseManager锛岀敤浜庡鍔犻澶栫殑鏈嶅姟鍣ㄧ‖浠朵俊鎭牎楠�
+ */
+public class CustomLicenseManager extends LicenseManager{
+    private static Logger logger = LogManager.getLogger(CustomLicenseManager.class);
+
+    //XML缂栫爜
+    private static final String XML_CHARSET = "UTF-8";
+    //榛樿BUFSIZE
+    private static final int DEFAULT_BUFSIZE = 8 * 1024;
+
+    public CustomLicenseManager() {
+
+    }
+
+    public CustomLicenseManager(LicenseParam param) {
+        super(param);
+    }
+
+    /**
+     * 澶嶅啓create鏂规硶
+     */
+    @Override
+    protected synchronized byte[] create(
+            LicenseContent content,
+            LicenseNotary notary)
+            throws Exception {
+        initialize(content);
+        this.validateCreate(content);
+        final GenericCertificate certificate = notary.sign(content);
+        return getPrivacyGuard().cert2key(certificate);
+    }
+
+    /**
+     * 澶嶅啓install鏂规硶锛屽叾涓璿alidate鏂规硶璋冪敤鏈被涓殑validate鏂规硶锛屾牎楠孖P鍦板潃銆丮ac鍦板潃绛夊叾浠栦俊鎭�
+     */
+    @Override
+    protected synchronized LicenseContent install(
+            final byte[] key,
+            final LicenseNotary notary)
+            throws Exception {
+        final GenericCertificate certificate = getPrivacyGuard().key2cert(key);
+
+        notary.verify(certificate);
+        final LicenseContent content = (LicenseContent)this.load(certificate.getEncoded());
+        this.validate(content);
+        setLicenseKey(key);
+        setCertificate(certificate);
+
+        return content;
+    }
+
+    /**
+     * 澶嶅啓verify鏂规硶锛岃皟鐢ㄦ湰绫讳腑鐨剉alidate鏂规硶锛屾牎楠孖P鍦板潃銆丮ac鍦板潃绛夊叾浠栦俊鎭�
+     */
+    @Override
+    protected synchronized LicenseContent verify(final LicenseNotary notary)
+            throws Exception {
+        GenericCertificate certificate = getCertificate();
+
+        // Load license key from preferences,
+        final byte[] key = getLicenseKey();
+        if (null == key){
+            throw new NoLicenseInstalledException(getLicenseParam().getSubject());
+        }
+
+        certificate = getPrivacyGuard().key2cert(key);
+        notary.verify(certificate);
+        final LicenseContent content = (LicenseContent)this.load(certificate.getEncoded());
+        this.validate(content);
+        setCertificate(certificate);
+
+        return content;
+    }
+
+    /**
+     * 鏍¢獙鐢熸垚璇佷功鐨勫弬鏁颁俊鎭�
+     */
+    protected synchronized void validateCreate(final LicenseContent content)
+            throws LicenseContentException {
+        final LicenseParam param = getLicenseParam();
+
+        final Date now = new Date();
+        final Date notBefore = content.getNotBefore();
+        final Date notAfter = content.getNotAfter();
+        if (null != notAfter && now.after(notAfter)){
+            throw new LicenseContentException("璇佷功澶辨晥鏃堕棿涓嶈兘鏃╀簬褰撳墠鏃堕棿");
+        }
+        if (null != notBefore && null != notAfter && notAfter.before(notBefore)){
+            throw new LicenseContentException("璇佷功鐢熸晥鏃堕棿涓嶈兘鏅氫簬璇佷功澶辨晥鏃堕棿");
+        }
+        final String consumerType = content.getConsumerType();
+        if (null == consumerType){
+            throw new LicenseContentException("鐢ㄦ埛绫诲瀷涓嶈兘涓虹┖");
+        }
+    }
+
+
+    /**
+     * 澶嶅啓validate鏂规硶锛屽鍔營P鍦板潃銆丮ac鍦板潃绛夊叾浠栦俊鎭牎楠�
+     */
+    @Override
+    protected synchronized void validate(final LicenseContent content)
+            throws LicenseContentException {
+        //1. 棣栧厛璋冪敤鐖剁被鐨剉alidate鏂规硶
+        super.validate(content);
+
+        //2. 鐒跺悗鏍¢獙鑷畾涔夌殑License鍙傛暟
+        //License涓彲琚厑璁哥殑鍙傛暟淇℃伅
+        LicenseCheck expectedCheckModel = (LicenseCheck) content.getExtra();
+        //褰撳墠鏈嶅姟鍣ㄧ湡瀹炵殑鍙傛暟淇℃伅
+        LicenseCheck serverCheckModel = getServerInfos();
+
+        if(expectedCheckModel != null && serverCheckModel != null){
+            //鏍¢獙IP鍦板潃
+            if(!checkIpAddress(expectedCheckModel.getIpAddress(),serverCheckModel.getIpAddress())){
+                throw new LicenseContentException("褰撳墠鏈嶅姟鍣ㄧ殑IP娌″湪鎺堟潈鑼冨洿鍐�");
+            }
+
+            //鏍¢獙Mac鍦板潃
+            if(!checkIpAddress(expectedCheckModel.getMacAddress(),serverCheckModel.getMacAddress())){
+                throw new LicenseContentException("褰撳墠鏈嶅姟鍣ㄧ殑Mac鍦板潃娌″湪鎺堟潈鑼冨洿鍐�");
+            }
+
+            //鏍¢獙涓绘澘搴忓垪鍙�
+            if(!checkSerial(expectedCheckModel.getMainBoardSerial(),serverCheckModel.getMainBoardSerial())){
+                throw new LicenseContentException("褰撳墠鏈嶅姟鍣ㄧ殑涓绘澘搴忓垪鍙锋病鍦ㄦ巿鏉冭寖鍥村唴");
+            }
+
+            //鏍¢獙CPU搴忓垪鍙�
+            if(!checkSerial(expectedCheckModel.getCpuSerial(),serverCheckModel.getCpuSerial())){
+                throw new LicenseContentException("褰撳墠鏈嶅姟鍣ㄧ殑CPU搴忓垪鍙锋病鍦ㄦ巿鏉冭寖鍥村唴");
+            }
+        }else{
+            throw new LicenseContentException("涓嶈兘鑾峰彇鏈嶅姟鍣ㄧ‖浠朵俊鎭�");
+        }
+    }
+
+
+    /**
+     * 閲嶅啓XMLDecoder瑙f瀽XML
+     */
+    private Object load(String encoded){
+        BufferedInputStream inputStream = null;
+        XMLDecoder decoder = null;
+        try {
+            inputStream = new BufferedInputStream(new ByteArrayInputStream(encoded.getBytes(XML_CHARSET)));
+
+            decoder = new XMLDecoder(new BufferedInputStream(inputStream, DEFAULT_BUFSIZE),null,null);
+
+            return decoder.readObject();
+        } catch (UnsupportedEncodingException e) {
+            e.printStackTrace();
+        } finally {
+            try {
+                if(decoder != null){
+                    decoder.close();
+                }
+                if(inputStream != null){
+                    inputStream.close();
+                }
+            } catch (Exception e) {
+                logger.error("XMLDecoder瑙f瀽XML澶辫触",e);
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * 鑾峰彇褰撳墠鏈嶅姟鍣ㄩ渶瑕侀澶栨牎楠岀殑License鍙傛暟
+     */
+    private LicenseCheck getServerInfos(){
+        //鎿嶄綔绯荤粺绫诲瀷
+        String osName = System.getProperty("os.name").toLowerCase();
+        AbstractServerInfos abstractServerInfos = null;
+
+        //鏍规嵁涓嶅悓鎿嶄綔绯荤粺绫诲瀷閫夋嫨涓嶅悓鐨勬暟鎹幏鍙栨柟娉�
+        if (osName.startsWith("windows")) {
+            abstractServerInfos = new WindowsServerInfos();
+        } else if (osName.startsWith("linux")) {
+            abstractServerInfos = new LinuxServerInfos();
+        }else{//鍏朵粬鏈嶅姟鍣ㄧ被鍨�
+            abstractServerInfos = new WindowsServerInfos();
+        }
+
+        return abstractServerInfos.getServerInfos();
+    }
+
+    /**
+     * 鏍¢獙褰撳墠鏈嶅姟鍣ㄧ殑IP/Mac鍦板潃鏄惁鍦ㄥ彲琚厑璁哥殑IP鑼冨洿鍐�<br/>
+     * 濡傛灉瀛樺湪IP鍦ㄥ彲琚厑璁哥殑IP/Mac鍦板潃鑼冨洿鍐咃紝鍒欒繑鍥瀟rue
+     */
+    private boolean checkIpAddress(List<String> expectedList,List<String> serverList){
+        if(expectedList != null && expectedList.size() > 0){
+            if(serverList != null && serverList.size() > 0){
+                for(String expected : expectedList){
+                    if(serverList.contains(expected.trim())){
+                        return true;
+                    }
+                }
+            }
+
+            return false;
+        }else {
+            return true;
+        }
+    }
+
+    /**
+     * 鏍¢獙褰撳墠鏈嶅姟鍣ㄧ‖浠讹紙涓绘澘銆丆PU绛夛級搴忓垪鍙锋槸鍚﹀湪鍙厑璁歌寖鍥村唴
+     */
+    private boolean checkSerial(String expectedSerial,String serverSerial){
+        if(!Cools.isEmpty(expectedSerial)){
+            if(!Cools.isEmpty(serverSerial)){
+                if(expectedSerial.equals(serverSerial)){
+                    return true;
+                }
+            }
+            return false;
+        }else{
+            return true;
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/zy/system/entity/license/LicenseCheck.java b/src/main/java/com/zy/system/entity/license/LicenseCheck.java
new file mode 100644
index 0000000..660f818
--- /dev/null
+++ b/src/main/java/com/zy/system/entity/license/LicenseCheck.java
@@ -0,0 +1,45 @@
+package com.zy.system.entity.license;
+
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 鑷畾涔夐渶瑕佹牎楠岀殑License鍙傛暟
+ */
+@Data
+public class LicenseCheck implements Serializable {
+
+    private static final long serialVersionUID = 8600137500316662317L;
+    /**
+     * 鍙鍏佽鐨処P鍦板潃
+     */
+    private List<String> ipAddress;
+
+    /**
+     * 鍙鍏佽鐨凪AC鍦板潃
+     */
+    private List<String> macAddress;
+
+    /**
+     * 鍙鍏佽鐨凜PU搴忓垪鍙�
+     */
+    private String cpuSerial;
+
+    /**
+     * 鍙鍏佽鐨勪富鏉垮簭鍒楀彿
+     */
+    private String mainBoardSerial;
+
+    @Override
+    public String toString() {
+        return "LicenseCheckModel{" +
+                "ipAddress=" + ipAddress +
+                ", macAddress=" + macAddress +
+                ", cpuSerial='" + cpuSerial + '\'' +
+                ", mainBoardSerial='" + mainBoardSerial + '\'' +
+                '}';
+    }
+
+}
diff --git a/src/main/java/com/zy/system/entity/license/LicenseCheckListener.java b/src/main/java/com/zy/system/entity/license/LicenseCheckListener.java
new file mode 100644
index 0000000..cf79568
--- /dev/null
+++ b/src/main/java/com/zy/system/entity/license/LicenseCheckListener.java
@@ -0,0 +1,122 @@
+package com.zy.system.entity.license;
+
+import com.core.common.Cools;
+import com.zy.system.entity.LicenseInfos;
+import com.zy.system.service.LicenseInfosService;
+import com.zy.system.timer.LicenseTimer;
+import de.schlichtherle.license.LicenseContent;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationListener;
+import org.springframework.context.event.ContextRefreshedEvent;
+import org.springframework.stereotype.Component;
+
+import java.util.Date;
+
+/**
+ * 鍦ㄩ」鐩惎鍔ㄦ椂瀹夎璇佷功
+ */
+@Component
+public class LicenseCheckListener implements ApplicationListener<ContextRefreshedEvent> {
+    private static Logger logger = LogManager.getLogger(LicenseCheckListener.class);
+
+    /**
+     * 璇佷功subject
+     */
+    @Value("${license.subject}")
+    private String subject;
+
+    /**
+     * 鍏挜鍒О
+     */
+    @Value("${license.publicAlias}")
+    private String publicAlias;
+
+    /**
+     * 璁块棶鍏挜搴撶殑瀵嗙爜
+     */
+    @Value("${license.storePass}")
+    private String storePass;
+
+    /**
+     * 璇佷功鐢熸垚璺緞
+     */
+    @Value("${license.licensePath}")
+    private String licensePath;
+
+    /**
+     * 瀵嗛挜搴撳瓨鍌ㄨ矾寰�
+     */
+    @Value("${license.publicKeysStorePath}")
+    private String publicKeysStorePath;
+    @Autowired
+    private LicenseTimer licenseTimer;
+    @Autowired
+    private LicenseInfosService licenseInfosService;
+
+    @Override
+    public void onApplicationEvent(ContextRefreshedEvent event) {
+        //root application context 娌℃湁parent
+        ApplicationContext context = event.getApplicationContext().getParent();
+        if(context == null){
+            loadLicense();
+        }
+    }
+
+    //鍔犺浇璇佷功
+    public boolean loadLicense() {
+        if(!Cools.isEmpty(licensePath)){
+            logger.info("++++++++ 寮�濮嬪姞杞借鍙瘉 ++++++++");
+
+            try {
+                licenseTimer.getRemoteLicense();
+            } catch (Exception e) {
+            }
+
+            try {
+                LicenseVerifyParam param = new LicenseVerifyParam();
+                param.setSubject(subject);
+                param.setPublicAlias(publicAlias);
+                param.setStorePass(storePass);
+                param.setLicensePath(licensePath);
+                param.setPublicKeysStorePath(publicKeysStorePath);
+
+                LicenseVerify licenseVerify = new LicenseVerify();
+
+                LicenseInfos latestLicense = licenseInfosService.getLatestLicense();
+                if (latestLicense == null) {
+                    logger.info("璁稿彲璇佷笉瀛樺湪");
+                    return false;
+                }
+
+                //瀹夎璇佷功
+                LicenseContent install = licenseVerify.install(param, latestLicense.getLicense());
+
+                logger.info("++++++++ 璁稿彲璇佸姞杞界粨鏉� ++++++++");
+
+                licenseTimer.setSystemSupport(install!=null);
+
+                if (install != null) {
+                    Date start = new Date();
+                    Date end = install.getNotAfter();
+                    Long starTime = start.getTime();
+                    Long endTime = end.getTime();
+                    Long num = endTime - starTime;//鏃堕棿鎴崇浉宸殑姣鏁�
+                    int day = (int) (num / 24 / 60 / 60 / 1000);
+                    licenseTimer.setLicenseDays(day);
+                }
+
+
+                return install != null;
+            } catch (Exception e) {
+                e.printStackTrace();
+                return false;
+            }
+        }
+        licenseTimer.setSystemSupport(false);
+        return false;
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/com/zy/system/entity/license/LicenseCreator.java b/src/main/java/com/zy/system/entity/license/LicenseCreator.java
new file mode 100644
index 0000000..01f2ad1
--- /dev/null
+++ b/src/main/java/com/zy/system/entity/license/LicenseCreator.java
@@ -0,0 +1,86 @@
+package com.zy.system.entity.license;
+
+import de.schlichtherle.license.*;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import javax.security.auth.x500.X500Principal;
+import java.io.File;
+import java.text.MessageFormat;
+import java.util.prefs.Preferences;
+
+/**
+ * License鐢熸垚绫�
+ */
+public class LicenseCreator {
+    private static Logger logger = LogManager.getLogger(LicenseCreator.class);
+    private final static X500Principal DEFAULT_HOLDER_AND_ISSUER = new X500Principal("CN=localhost, OU=localhost, O=localhost, L=SH, ST=SH, C=CN");
+    private LicenseCreatorParam param;
+
+    public LicenseCreator(LicenseCreatorParam param) {
+        this.param = param;
+    }
+
+    /**
+     * 鐢熸垚License璇佷功
+     */
+    public boolean generateLicense(){
+        try {
+            LicenseManager licenseManager = new CustomLicenseManager(initLicenseParam());
+            LicenseContent licenseContent = initLicenseContent();
+
+            licenseManager.store(licenseContent,new File(param.getLicensePath()));
+
+            return true;
+        }catch (Exception e){
+            logger.error(MessageFormat.format("璇佷功鐢熸垚澶辫触锛歿0}",param),e);
+            return false;
+        }
+    }
+
+    /**
+     * 鍒濆鍖栬瘉涔︾敓鎴愬弬鏁�
+     */
+    private LicenseParam initLicenseParam(){
+        Preferences preferences = Preferences.userNodeForPackage(LicenseCreator.class);
+
+        //璁剧疆瀵硅瘉涔﹀唴瀹瑰姞瀵嗙殑绉橀挜
+        CipherParam cipherParam = new DefaultCipherParam(param.getStorePass());
+
+        KeyStoreParam privateStoreParam = new CustomKeyStoreParam(LicenseCreator.class
+                ,param.getPrivateKeysStorePath()
+                ,param.getPrivateAlias()
+                ,param.getStorePass()
+                ,param.getKeyPass());
+
+        LicenseParam licenseParam = new DefaultLicenseParam(param.getSubject()
+                ,preferences
+                ,privateStoreParam
+                ,cipherParam);
+
+        return licenseParam;
+    }
+
+    /**
+     * 璁剧疆璇佷功鐢熸垚姝f枃淇℃伅
+     */
+    private LicenseContent initLicenseContent(){
+        LicenseContent licenseContent = new LicenseContent();
+        licenseContent.setHolder(DEFAULT_HOLDER_AND_ISSUER);
+        licenseContent.setIssuer(DEFAULT_HOLDER_AND_ISSUER);
+
+        licenseContent.setSubject(param.getSubject());
+        licenseContent.setIssued(param.getIssuedTime());
+        licenseContent.setNotBefore(param.getIssuedTime());
+        licenseContent.setNotAfter(param.getExpiryTime());
+        licenseContent.setConsumerType(param.getConsumerType());
+        licenseContent.setConsumerAmount(param.getConsumerAmount());
+        licenseContent.setInfo(param.getDescription());
+
+        //鎵╁睍鏍¢獙鏈嶅姟鍣ㄧ‖浠朵俊鎭�
+        licenseContent.setExtra(param.getLicenseCheck());
+
+        return licenseContent;
+    }
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/zy/system/entity/license/LicenseCreatorParam.java b/src/main/java/com/zy/system/entity/license/LicenseCreatorParam.java
new file mode 100644
index 0000000..5a3915d
--- /dev/null
+++ b/src/main/java/com/zy/system/entity/license/LicenseCreatorParam.java
@@ -0,0 +1,96 @@
+package com.zy.system.entity.license;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * License鐢熸垚绫婚渶瑕佺殑鍙傛暟
+ */
+@Data
+public class LicenseCreatorParam implements Serializable {
+
+    private static final long serialVersionUID = -7793154252684580872L;
+    /**
+     * 璇佷功subject
+     */
+    private String subject;
+
+    /**
+     * 瀵嗛挜鍒О
+     */
+    private String privateAlias;
+
+    /**
+     * 瀵嗛挜瀵嗙爜锛堥渶瑕佸Ε鍠勪繚绠★紝涓嶈兘璁╀娇鐢ㄨ�呯煡閬擄級
+     */
+    private String keyPass;
+
+    /**
+     * 璁块棶绉橀挜搴撶殑瀵嗙爜
+     */
+    private String storePass;
+
+    /**
+     * 璇佷功鐢熸垚璺緞
+     */
+    private String licensePath;
+
+    /**
+     * 瀵嗛挜搴撳瓨鍌ㄨ矾寰�
+     */
+    private String privateKeysStorePath;
+
+    /**
+     * 璇佷功鐢熸晥鏃堕棿
+     */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date issuedTime = new Date();
+
+    /**
+     * 璇佷功澶辨晥鏃堕棿
+     */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date expiryTime;
+
+    /**
+     * 鐢ㄦ埛绫诲瀷
+     */
+    private String consumerType = "user";
+
+    /**
+     * 鐢ㄦ埛鏁伴噺
+     */
+    private Integer consumerAmount = 1;
+
+    /**
+     * 鎻忚堪淇℃伅
+     */
+    private String description = "";
+
+    /**
+     * 棰濆鐨勬湇鍔″櫒纭欢鏍¢獙淇℃伅
+     */
+    private LicenseCheck licenseCheck;
+
+    @Override
+    public String toString() {
+        return "LicenseCreatorParam{" +
+                "subject='" + subject + '\'' +
+                ", privateAlias='" + privateAlias + '\'' +
+                ", keyPass='" + keyPass + '\'' +
+                ", storePass='" + storePass + '\'' +
+                ", licensePath='" + licensePath + '\'' +
+                ", privateKeysStorePath='" + privateKeysStorePath + '\'' +
+                ", issuedTime=" + issuedTime +
+                ", expiryTime=" + expiryTime +
+                ", consumerType='" + consumerType + '\'' +
+                ", consumerAmount=" + consumerAmount +
+                ", description='" + description + '\'' +
+                ", licenseCheck=" + licenseCheck +
+                '}';
+    }
+
+}
diff --git a/src/main/java/com/zy/system/entity/license/LicenseManagerHolder.java b/src/main/java/com/zy/system/entity/license/LicenseManagerHolder.java
new file mode 100644
index 0000000..64c6f39
--- /dev/null
+++ b/src/main/java/com/zy/system/entity/license/LicenseManagerHolder.java
@@ -0,0 +1,22 @@
+package com.zy.system.entity.license;
+
+import de.schlichtherle.license.LicenseManager;
+import de.schlichtherle.license.LicenseParam;
+
+public class LicenseManagerHolder {
+
+    private static volatile LicenseManager LICENSE_MANAGER;
+
+    public static LicenseManager getInstance(LicenseParam param) {
+        if (LICENSE_MANAGER == null) {
+            synchronized (LicenseManagerHolder.class) {
+                if (LICENSE_MANAGER == null) {
+                    LICENSE_MANAGER = new CustomLicenseManager(param);
+                }
+            }
+        }
+
+        return LICENSE_MANAGER;
+    }
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/zy/system/entity/license/LicenseVerify.java b/src/main/java/com/zy/system/entity/license/LicenseVerify.java
new file mode 100644
index 0000000..da239c0
--- /dev/null
+++ b/src/main/java/com/zy/system/entity/license/LicenseVerify.java
@@ -0,0 +1,126 @@
+package com.zy.system.entity.license;
+
+import de.schlichtherle.license.*;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.text.DateFormat;
+import java.text.MessageFormat;
+import java.text.SimpleDateFormat;
+import java.util.Base64;
+import java.util.prefs.Preferences;
+
+/**
+ * License鏍¢獙绫�
+ */
+public class LicenseVerify {
+    private static Logger logger = LogManager.getLogger(LicenseVerify.class);
+
+    /**
+     * 瀹夎License璇佷功
+     */
+    public synchronized LicenseContent install(LicenseVerifyParam param, String license) {
+        LicenseContent result = null;
+        DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+
+        //1. 瀹夎璇佷功
+        try {
+            LicenseParam licenseParam = initLicenseParam(param);
+            LicenseManager licenseManager = LicenseManagerHolder.getInstance(licenseParam);
+            licenseManager.uninstall();
+
+            File tempFileFromBase64 = createTempFileFromBase64(license);
+            result = licenseManager.install(tempFileFromBase64);
+            logger.info(MessageFormat.format("璁稿彲璇佸姞杞芥垚鍔燂紝璁稿彲璇佹湁鏁堟湡锛歿0} - {1}", format.format(result.getNotBefore()), format.format(result.getNotAfter())));
+        } catch (Exception e) {
+            logger.error("璁稿彲璇佸姞杞藉け璐ワ紒", e);
+        }
+
+        return result;
+    }
+
+    /**
+     * 鏍¢獙License璇佷功
+     */
+    public boolean verify(){
+        try {
+            LicenseManager licenseManager = LicenseManagerHolder.getInstance(null);
+            DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+
+            LicenseContent licenseContent = licenseManager.verify();
+            logger.info(MessageFormat.format("璁稿彲璇佹牎楠岄�氳繃锛岃鍙瘉鏈夋晥鏈燂細{0} - {1}",format.format(licenseContent.getNotBefore()),format.format(licenseContent.getNotAfter())));
+            return true;
+        }catch (Exception e){
+            logger.error("璁稿彲璇佹牎楠屽け璐ワ紒",e);
+            return false;
+        }
+    }
+
+    /**
+     * 鏍¢獙License璇佷功骞惰幏鍙栬瘉涔︿俊鎭�
+     */
+    public LicenseContent getVerifyInfo(){
+        LicenseManager licenseManager = LicenseManagerHolder.getInstance(null);
+
+        //鏍¢獙璇佷功
+        try {
+            LicenseContent licenseContent = licenseManager.verify();
+            return licenseContent;
+        }catch (Exception e){
+            logger.error("璁稿彲璇佹牎楠屽け璐ワ紒",e);
+            return null;
+        }
+    }
+
+    /**
+     * 鍒濆鍖栬瘉涔︾敓鎴愬弬鏁�
+     * @param param License鏍¢獙绫婚渶瑕佺殑鍙傛暟
+     * @return de.schlichtherle.license.LicenseParam
+     */
+    private LicenseParam initLicenseParam(LicenseVerifyParam param){
+        Preferences preferences = Preferences.userNodeForPackage(LicenseVerify.class);
+
+        CipherParam cipherParam = new DefaultCipherParam(param.getStorePass());
+
+        KeyStoreParam publicStoreParam = new CustomKeyStoreParam(LicenseVerify.class
+                ,param.getPublicKeysStorePath()
+                ,param.getPublicAlias()
+                ,param.getStorePass()
+                ,null);
+
+        return new DefaultLicenseParam(param.getSubject()
+                ,preferences
+                ,publicStoreParam
+                ,cipherParam);
+    }
+
+    /**
+     * 灏咮ase64瀛楃涓茶浆鎹负涓存椂鏂囦欢
+     * @param base64String Base64缂栫爜鐨勫瓧绗︿覆
+     * @param filePrefix 鏂囦欢鍚嶅墠缂�锛堜緥濡� "license_"锛�
+     * @param fileSuffix 鏂囦欢鍚庣紑锛堜緥濡� ".lic"锛�
+     * @return 鐢熸垚鐨勪复鏃禙ile瀵硅薄锛堣嚜鍔ㄥ湪JVM閫�鍑烘椂鍒犻櫎锛�
+     * @throws IOException
+     */
+    public File base64ToTempFile(String base64String, String filePrefix, String fileSuffix)
+            throws IOException {
+        // 瑙g爜Base64
+        byte[] decodedBytes = Base64.getDecoder().decode(base64String);
+        // 鍒涘缓涓存椂鏂囦欢
+        Path tempPath = Files.createTempFile(filePrefix, fileSuffix);
+        // 鍐欏叆鍐呭
+        Files.write(tempPath, decodedBytes);
+        // 璁剧疆JVM閫�鍑烘椂鑷姩鍒犻櫎
+        tempPath.toFile().deleteOnExit();
+        return tempPath.toFile();
+    }
+
+    public File createTempFileFromBase64(String base64Data) throws IOException {
+        return base64ToTempFile(base64Data, "temp_license_", ".bin");
+    }
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/zy/system/entity/license/LicenseVerifyParam.java b/src/main/java/com/zy/system/entity/license/LicenseVerifyParam.java
new file mode 100644
index 0000000..33806e5
--- /dev/null
+++ b/src/main/java/com/zy/system/entity/license/LicenseVerifyParam.java
@@ -0,0 +1,48 @@
+package com.zy.system.entity.license;
+
+import lombok.Data;
+
+/**
+ * 鏍¢獙绛惧悕鏂囦欢
+ */
+@Data
+public class LicenseVerifyParam {
+
+    /**
+     * 璇佷功subject
+     */
+    private String subject;
+
+    /**
+     * 鍏挜鍒О
+     */
+    private String publicAlias;
+
+    /**
+     * 璁块棶鍏挜搴撶殑瀵嗙爜
+     */
+    private String storePass;
+
+    /**
+     * 璇佷功鐢熸垚璺緞
+     */
+    private String licensePath;
+
+    /**
+     * 瀵嗛挜搴撳瓨鍌ㄨ矾寰�
+     */
+    private String publicKeysStorePath;
+
+    public LicenseVerifyParam() {
+
+    }
+
+    public LicenseVerifyParam(String subject, String publicAlias, String storePass, String licensePath, String publicKeysStorePath) {
+        this.subject = subject;
+        this.publicAlias = publicAlias;
+        this.storePass = storePass;
+        this.licensePath = licensePath;
+        this.publicKeysStorePath = publicKeysStorePath;
+    }
+
+}
diff --git a/src/main/java/com/zy/system/entity/license/LinuxServerInfos.java b/src/main/java/com/zy/system/entity/license/LinuxServerInfos.java
new file mode 100644
index 0000000..0f40daf
--- /dev/null
+++ b/src/main/java/com/zy/system/entity/license/LinuxServerInfos.java
@@ -0,0 +1,91 @@
+package com.zy.system.entity.license;
+
+import com.core.common.Cools;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.net.InetAddress;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * 鐢ㄤ簬鑾峰彇瀹㈡埛Linux鏈嶅姟鍣ㄧ殑鍩烘湰淇℃伅
+ */
+public class LinuxServerInfos extends AbstractServerInfos {
+
+    @Override
+    protected List<String> getIpAddress() throws Exception {
+        List<String> result = null;
+
+        //鑾峰彇鎵�鏈夌綉缁滄帴鍙�
+        List<InetAddress> inetAddresses = getLocalAllInetAddress();
+
+        if (inetAddresses != null && inetAddresses.size() > 0) {
+            result = inetAddresses.stream().map(InetAddress::getHostAddress).distinct().map(String::toLowerCase).collect(Collectors.toList());
+        }
+
+        return result;
+    }
+
+    @Override
+    protected List<String> getMacAddress() throws Exception {
+        List<String> result = null;
+
+        //1. 鑾峰彇鎵�鏈夌綉缁滄帴鍙�
+        List<InetAddress> inetAddresses = getLocalAllInetAddress();
+
+        if (inetAddresses != null && inetAddresses.size() > 0) {
+            //2. 鑾峰彇鎵�鏈夌綉缁滄帴鍙g殑Mac鍦板潃
+            result = inetAddresses.stream().map(this::getMacByInetAddress).distinct().collect(Collectors.toList());
+        }
+
+        return result;
+    }
+
+    @Override
+    protected String getCPUSerial() throws Exception {
+        //搴忓垪鍙�
+        String serialNumber = "";
+
+        //浣跨敤dmidecode鍛戒护鑾峰彇CPU搴忓垪鍙�
+        String[] shell = {"/bin/bash", "-c", "dmidecode -t processor | grep 'ID' | awk -F ':' '{print $2}' | head -n 1"};
+        Process process = Runtime.getRuntime().exec(shell);
+        process.getOutputStream().close();
+
+        BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
+
+        if (null == reader.readLine()) {
+            return serialNumber;
+        }
+        String line = reader.readLine().trim();
+        if (!Cools.isEmpty(line)) {
+            serialNumber = line;
+        }
+
+        reader.close();
+        return serialNumber;
+    }
+
+    @Override
+    protected String getMainBoardSerial() throws Exception {
+        //搴忓垪鍙�
+        String serialNumber = "";
+
+        //浣跨敤dmidecode鍛戒护鑾峰彇涓绘澘搴忓垪鍙�
+        String[] shell = {"/bin/bash", "-c", "dmidecode | grep 'Serial Number' | awk -F ':' '{print $2}' | head -n 1"};
+        Process process = Runtime.getRuntime().exec(shell);
+        process.getOutputStream().close();
+
+        BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
+        if (null == reader.readLine()) {
+            return serialNumber;
+        }
+        String line = reader.readLine().trim();
+        if (!Cools.isEmpty(line)) {
+            serialNumber = line;
+        }
+
+        reader.close();
+        return serialNumber;
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/com/zy/system/entity/license/WindowsServerInfos.java b/src/main/java/com/zy/system/entity/license/WindowsServerInfos.java
new file mode 100644
index 0000000..c575e6d
--- /dev/null
+++ b/src/main/java/com/zy/system/entity/license/WindowsServerInfos.java
@@ -0,0 +1,85 @@
+package com.zy.system.entity.license;
+
+import java.net.InetAddress;
+import java.util.List;
+import java.util.Scanner;
+import java.util.stream.Collectors;
+
+/**
+ * 鐢ㄤ簬鑾峰彇瀹㈡埛Windows鏈嶅姟鍣ㄧ殑鍩烘湰淇℃伅
+ */
+public class WindowsServerInfos extends AbstractServerInfos {
+
+    @Override
+    protected List<String> getIpAddress() throws Exception {
+        List<String> result = null;
+
+        //鑾峰彇鎵�鏈夌綉缁滄帴鍙�
+        List<InetAddress> inetAddresses = getLocalAllInetAddress();
+
+        if(inetAddresses != null && inetAddresses.size() > 0){
+            result = inetAddresses.stream().map(InetAddress::getHostAddress).distinct().map(String::toLowerCase).collect(Collectors.toList());
+        }
+
+        return result;
+    }
+
+    @Override
+    protected List<String> getMacAddress() throws Exception {
+        List<String> result = null;
+
+        //1. 鑾峰彇鎵�鏈夌綉缁滄帴鍙�
+        List<InetAddress> inetAddresses = getLocalAllInetAddress();
+
+        if(inetAddresses != null && inetAddresses.size() > 0){
+            //2. 鑾峰彇鎵�鏈夌綉缁滄帴鍙g殑Mac鍦板潃
+            result = inetAddresses.stream().map(this::getMacByInetAddress).distinct().collect(Collectors.toList());
+        }
+
+        return result;
+    }
+
+    @Override
+    protected String getCPUSerial() throws Exception {
+        //搴忓垪鍙�
+        String serialNumber = "";
+
+        //浣跨敤WMIC鑾峰彇CPU搴忓垪鍙�
+        Process process = Runtime.getRuntime().exec("wmic cpu get processorid");
+        process.getOutputStream().close();
+        Scanner scanner = new Scanner(process.getInputStream());
+
+        if(scanner.hasNext()){
+            scanner.next();
+        }
+
+        if(scanner.hasNext()){
+            serialNumber = scanner.next().trim();
+        }
+
+        scanner.close();
+        return serialNumber;
+    }
+
+    @Override
+    protected String getMainBoardSerial() throws Exception {
+        //搴忓垪鍙�
+        String serialNumber = "";
+
+        //浣跨敤WMIC鑾峰彇涓绘澘搴忓垪鍙�
+        Process process = Runtime.getRuntime().exec("wmic baseboard get serialnumber");
+        process.getOutputStream().close();
+        Scanner scanner = new Scanner(process.getInputStream());
+
+        if(scanner.hasNext()){
+            scanner.next();
+        }
+
+        if(scanner.hasNext()){
+            serialNumber = scanner.next().trim();
+        }
+
+        scanner.close();
+        return serialNumber;
+    }
+}
\ No newline at end of file
diff --git a/src/main/java/com/zy/system/mapper/LicenseInfosMapper.java b/src/main/java/com/zy/system/mapper/LicenseInfosMapper.java
new file mode 100644
index 0000000..7a47079
--- /dev/null
+++ b/src/main/java/com/zy/system/mapper/LicenseInfosMapper.java
@@ -0,0 +1,14 @@
+package com.zy.system.mapper;
+
+import com.baomidou.mybatisplus.mapper.BaseMapper;
+import com.zy.system.entity.LicenseInfos;
+import org.apache.ibatis.annotations.Mapper;
+import org.springframework.stereotype.Repository;
+
+@Mapper
+@Repository
+public interface LicenseInfosMapper extends BaseMapper<LicenseInfos> {
+
+    LicenseInfos getLatestLicense();
+
+}
diff --git a/src/main/java/com/zy/system/service/LicenseInfosService.java b/src/main/java/com/zy/system/service/LicenseInfosService.java
new file mode 100644
index 0000000..75fdaab
--- /dev/null
+++ b/src/main/java/com/zy/system/service/LicenseInfosService.java
@@ -0,0 +1,10 @@
+package com.zy.system.service;
+
+import com.baomidou.mybatisplus.service.IService;
+import com.zy.system.entity.LicenseInfos;
+
+public interface LicenseInfosService extends IService<LicenseInfos> {
+
+    LicenseInfos getLatestLicense();
+
+}
diff --git a/src/main/java/com/zy/system/service/impl/LicenseInfosServiceImpl.java b/src/main/java/com/zy/system/service/impl/LicenseInfosServiceImpl.java
new file mode 100644
index 0000000..491a6c2
--- /dev/null
+++ b/src/main/java/com/zy/system/service/impl/LicenseInfosServiceImpl.java
@@ -0,0 +1,16 @@
+package com.zy.system.service.impl;
+
+import com.baomidou.mybatisplus.service.impl.ServiceImpl;
+import com.zy.system.entity.LicenseInfos;
+import com.zy.system.mapper.LicenseInfosMapper;
+import com.zy.system.service.LicenseInfosService;
+import org.springframework.stereotype.Service;
+
+@Service("licenseInfosService")
+public class LicenseInfosServiceImpl extends ServiceImpl<LicenseInfosMapper, LicenseInfos> implements LicenseInfosService {
+
+    @Override
+    public LicenseInfos getLatestLicense() {
+        return this.baseMapper.getLatestLicense();
+    }
+}
diff --git a/src/main/java/com/zy/system/timer/LicenseTimer.java b/src/main/java/com/zy/system/timer/LicenseTimer.java
new file mode 100644
index 0000000..dc49eb7
--- /dev/null
+++ b/src/main/java/com/zy/system/timer/LicenseTimer.java
@@ -0,0 +1,162 @@
+package com.zy.system.timer;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.zy.common.utils.HttpHandler;
+import com.zy.system.entity.LicenseInfos;
+import com.zy.system.entity.license.*;
+import com.zy.system.service.LicenseInfosService;
+import de.schlichtherle.license.LicenseContent;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+
+import java.util.Date;
+import java.util.HashMap;
+
+@Component
+public class LicenseTimer {
+
+    private static boolean SYSTEM_SUPPORT = false;//绯荤粺婵�娲荤姸鎬侊紝榛樿鍏抽棴
+
+    private static int LICENSE_DAYS = 0;//璁稿彲璇佸ぉ鏁�
+
+    /**
+     * 璇佷功subject
+     */
+    @Value("${license.subject}")
+    private String subject;
+
+    /**
+     * 鍏挜鍒О
+     */
+    @Value("${license.publicAlias}")
+    private String publicAlias;
+
+    /**
+     * 璁块棶鍏挜搴撶殑瀵嗙爜
+     */
+    @Value("${license.storePass}")
+    private String storePass;
+
+    /**
+     * 璇佷功鐢熸垚璺緞
+     */
+    @Value("${license.licensePath}")
+    private String licensePath;
+
+    /**
+     * 瀵嗛挜搴撳瓨鍌ㄨ矾寰�
+     */
+    @Value("${license.publicKeysStorePath}")
+    private String publicKeysStorePath;
+
+    @Autowired
+    private LicenseInfosService licenseInfosService;
+
+    //姣忓ぉ鏅氫笂11鐐规洿鏂扮郴缁熸縺娲荤姸鎬�
+    @Scheduled(cron = "0 0 23 * * ? ")
+    public void timer() {
+        try {
+            getRemoteLicense();
+        } catch (Exception e) {
+
+        }
+
+        try {
+            verify();
+        } catch (Exception e) {
+
+        }
+    }
+
+    public void getRemoteLicense() {
+        try {
+            AbstractServerInfos abstractServerInfos = null;
+            String osName = System.getProperty("os.name");
+            //鏍规嵁涓嶅悓鎿嶄綔绯荤粺绫诲瀷閫夋嫨涓嶅悓鐨勬暟鎹幏鍙栨柟娉�
+            if (osName.startsWith("windows")) {
+                abstractServerInfos = new WindowsServerInfos();
+            } else if (osName.startsWith("linux")) {
+                abstractServerInfos = new LinuxServerInfos();
+            }else{//鍏朵粬鏈嶅姟鍣ㄧ被鍨�
+                abstractServerInfos = new WindowsServerInfos();
+            }
+            LicenseCheck serverInfos = abstractServerInfos.getServerInfos();
+
+            HashMap<String, Object> map = new HashMap<>();
+            map.put("subject", subject);
+            map.put("licenseCheck", serverInfos);
+
+            String response = new HttpHandler.Builder()
+                    .setUri("http://net.zoneyung.net:9999/license")
+                    .setPath("/remoteQueryLicense")
+                    .setJson(JSON.toJSONString(map))
+                    .build()
+                    .doPost();
+            JSONObject jsonObject = JSON.parseObject(response);
+            if (jsonObject.getString("result").equals("ok")) {
+                LicenseInfos licenseInfos = new LicenseInfos();
+                licenseInfos.setLicense(jsonObject.getString("data"));
+                licenseInfos.setCreateTime(new Date());
+                licenseInfos.setLicenseTime(jsonObject.getString("licenseTime"));
+                licenseInfosService.insert(licenseInfos);
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    public void verify() {
+        LicenseInfos latestLicense = licenseInfosService.getLatestLicense();
+        if (latestLicense == null) {
+            setLicenseDays(0);
+            setSystemSupport(false);
+            return;
+        }
+
+        LicenseVerifyParam param = new LicenseVerifyParam();
+        param.setSubject(subject);
+        param.setPublicAlias(publicAlias);
+        param.setStorePass(storePass);
+        param.setLicensePath(licensePath);
+        param.setPublicKeysStorePath(publicKeysStorePath);
+
+        //楠岃瘉璁稿彲璇佹槸鍚︽湁鏁�
+        LicenseVerify licenseVerify = new LicenseVerify();
+        //瀹夎璇佷功
+        LicenseContent install = licenseVerify.install(param, latestLicense.getLicense());
+
+        if (install != null) {
+            Date start = new Date();
+            Date end = install.getNotAfter();
+            Long starTime = start.getTime();
+            Long endTime = end.getTime();
+            long num = endTime - starTime;//鏃堕棿鎴崇浉宸殑姣鏁�
+            int day = (int) (num / 24 / 60 / 60 / 1000);
+            setLicenseDays(day);
+            setSystemSupport(true);
+        }else {
+            setLicenseDays(0);
+            setSystemSupport(false);
+        }
+    }
+
+    public boolean getSystemSupport() {
+        return SYSTEM_SUPPORT;
+    }
+
+    public void setSystemSupport(boolean systemSupport) {
+        SYSTEM_SUPPORT = systemSupport;
+    }
+
+    public int getLicenseDays() {
+        return LICENSE_DAYS;
+    }
+
+    public void setLicenseDays(int licenseDays) {
+        LICENSE_DAYS = licenseDays;
+    }
+
+}
diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml
index a4175b0..4d337f9 100644
--- a/src/main/resources/application.yml
+++ b/src/main/resources/application.yml
@@ -48,6 +48,14 @@
 swagger:
   enable: false
 
+#License鐩稿叧閰嶇疆
+license:
+  subject: mdqdasrs
+  publicAlias: publicCert
+  storePass: public_zhongyang_123456789
+  licensePath: license.lic
+  publicKeysStorePath: publicCerts.keystore
+
 # 涓嬩綅鏈洪厤缃�
 wcs-slave:
   # 鍙屾繁
diff --git a/src/main/resources/mapper/LicenseInfosMapper.xml b/src/main/resources/mapper/LicenseInfosMapper.xml
new file mode 100644
index 0000000..368c596
--- /dev/null
+++ b/src/main/resources/mapper/LicenseInfosMapper.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.zy.system.mapper.LicenseInfosMapper">
+
+    <!-- 閫氱敤鏌ヨ鏄犲皠缁撴灉 -->
+    <resultMap id="BaseResultMap" type="com.zy.system.entity.LicenseInfos">
+        <id column="id" property="id" />
+        <result column="license" property="license" />
+        <result column="license_time" property="licenseTime" />
+        <result column="create_time" property="createTime" />
+
+    </resultMap>
+
+    <select id="getLatestLicense" resultMap="BaseResultMap">
+        select top 1 * from dbo.sys_license_infos order by create_time desc
+    </select>
+
+</mapper>
diff --git a/src/main/resources/publicCerts.keystore b/src/main/resources/publicCerts.keystore
new file mode 100644
index 0000000..382a874
--- /dev/null
+++ b/src/main/resources/publicCerts.keystore
Binary files differ

--
Gitblit v1.9.1