文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

软件产品license的简单实现java

2023-08-16 20:52

关注

我们在使用一些需要购买版权的软件产品时,或者我们做的商业软件需要进行售卖,为了收取费用,一般需要一个软件使用许可证,然后输入这个许可到软件里就能够使用软件。简单的是一串序列码或者一个许可证文件,复杂的是一个定制化插件包。于是有的小伙伴就开始好奇这个许可是怎么实现的,特别是在离线情况下它是怎么给软件授权,同时又能避免被破解的。

本文主要介绍的是许可证形式的授权。

1. 如何控制只在指定服务器上使用

如果不控制指定设备,那么下发了许可证,只要把软件复制多份安装则可到处使用,不利于版权维护,每个设备都有唯一标识:mac地址,ip地址,主板序列号等,在许可证中指定唯一标识则只能指定设备使用。

2. 如何控制软件使用期限

为了版权可持续性收益,对软件使用设置期限,到期续费等,则需要在许可证中配置使用起止日期。

3. 如何控制按收费等级分模块使用功能

售卖的软件产品可能进行分级收费,不同的收费标准可使用的软件功能范围不同。许可证中配置可使用的功能模块节点,前提是软件产品在设计开发时,需要有对应的模块结构代码。

一、流程设计

实现流程,直接上图!!!
license实现流程

私用工具端:
RSA秘钥对生成

package com.license.tools.licensecreate.utils;import java.security.KeyPair;import java.security.KeyPairGenerator;import java.security.NoSuchAlgorithmException;import java.security.interfaces.RSAPrivateKey;import java.security.interfaces.RSAPublicKey;import java.util.Base64;public class KeyGenerator {        private static byte[] privateKey;        private static byte[] publicKey;        private static final String KEY_ALGORITHM = "RSA";    public void generater() {        try {            KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KEY_ALGORITHM);            keyPairGenerator.initialize(1024);            KeyPair keyPair = keyPairGenerator.genKeyPair();            RSAPublicKey pubKey = (RSAPublicKey) keyPair.getPublic();            RSAPrivateKey priKey = (RSAPrivateKey) keyPair.getPrivate();            privateKey = Base64.getEncoder().encode(priKey.getEncoded());            publicKey = Base64.getEncoder().encode(pubKey.getEncoded());            System.out.println("公钥:" + new String(publicKey));            System.out.println("私钥:" + new String(privateKey));        } catch (NoSuchAlgorithmException e) {            e.printStackTrace();            System.out.println("生成密钥对失败!");        }    }    public static void main(String[] args) {        KeyGenerator keyGenerator = new KeyGenerator();        keyGenerator.generater();    }}

运行main方法,生成的秘钥对填入RSAUtils的puk和prk,公钥用于加密,私钥用于解密

package com.license.tools.licensecreate.utils;import javax.crypto.BadPaddingException;import javax.crypto.Cipher;import javax.crypto.IllegalBlockSizeException;import javax.crypto.NoSuchPaddingException;import java.security.*;import java.security.spec.InvalidKeySpecException;import java.security.spec.PKCS8EncodedKeySpec;import java.security.spec.X509EncodedKeySpec;import java.util.Base64;public class RSAUtils {        private static String puk = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCdfujgTmG4aOa4oK2VysmKvAI+hurN/wuKQjzgJTo3ct6TH5NHFHncb9KXijC1xk2Po+pJ8UjU4XGjU4gq5yhTdeSYPYR6hj5jqLy8fkWpFzeC6RvM4bLDe1lDNKphpcUoo5ZO7T77w9fX2lgJSyy/8LxdBThc4Megga3KW1/W4wIDAQAB";        protected static String encrypt(String content) throws Exception {        byte[] publicKeyBytes = puk.getBytes();        X509EncodedKeySpec x = new X509EncodedKeySpec(Base64.getDecoder().decode(publicKeyBytes));        KeyFactory keyFactory = KeyFactory.getInstance("RSA");        PublicKey pubKey = keyFactory.generatePublic(x);        Cipher cipher = Cipher.getInstance("RSA");        cipher.init(Cipher.ENCRYPT_MODE, pubKey);        byte[] result = cipher.doFinal(content.getBytes("UTF-8"));        return Base64.getEncoder().encodeToString(result);    }        private static String prk = "MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAJ1+6OBOYbho5rigrZXKyYq8Aj6G6s3/C4pCPOAlOjdy3pMfk0cUedxv0peKMLXGTY+j6knxSNThcaNTiCrnKFN15Jg9hHqGPmOovLx+RakXN4LpG8zhssN7WUM0qmGlxSijlk7tPvvD19faWAlLLL/wvF0FOFzgx6CBrcpbX9bjAgMBAAECgYA8uRWohg//PdLXFHxY6JrUNrDW0sXtLoyQfgFimnfbsRpHt0DdgvOJHkQf0VP+gbqdyyEl6TWfflyGEErL39wX1rrosy+LpiN0HeISERJuwJtuiGeR+0qw+Xz2M7VE+e5oD94dRtlzERft2mcDbQAQYUCFNgUBtd1dCJgMJPZJYQJBANHxKKHqMbsH91JsGP8eCu+yeMah0X8cT79nwD71SJRc03W5P1MPKhRyGWJj0M+Wax32pAPCMTfbj19scLplJpUCQQDADD5OuSLYRVqx68/CYbFVK3ye/YD4Cgc+0kT9SoI9bLB10JumHT0seDGeXQqwUPAF3bBZGI8pW2bdtzDj8YGXAkABQXgEv+ncPIf2Lj9YB035cQ/X4E/oerrfYjd8KOtuN7/sDFecn5KY3LXaKM6u7y9k1nzUqOyycNXCtFtYQhKhAkBvgyxyvaFz/uFoyko6zksP705Pa1eFrx0B50pT4P26+O+FmXmnfPbWaXw2PkREmNqmLVGGinImS4JxXzuuP79FAkAFQejjE+5Twi8oSCcNwse7FFP86U6jgcc+S+XCUUkLXQ5SPlkyb037hwoV1lEEJpcyI2tSFRxBKT89KZN0Nfat";    protected static String decrypt(String signEncrypt) throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {        byte[] privateKeyBytes = prk.getBytes();        PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKeyBytes));        KeyFactory keyFactory = KeyFactory.getInstance("RSA");        PrivateKey priKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);        Cipher cipher = Cipher.getInstance("RSA");        cipher.init(Cipher.DECRYPT_MODE, priKey);        byte[] result = cipher.doFinal(Base64.getDecoder().decode(signEncrypt));        return new String(result);    }    public static void main(String[] args) throws Exception {        String password = "123456";        String a = encrypt(password);        System.out.println("AES加密秘钥:" + a);    }}

main方法输入AES需要的明文(示例:123456),用RSA加密成密文,做为AESUtils的aesKey

package com.license.tools.licensecreate.utils;import org.springframework.util.Base64Utils;import javax.crypto.KeyGenerator;import javax.crypto.*;import javax.crypto.spec.SecretKeySpec;import java.security.InvalidKeyException;import java.security.NoSuchAlgorithmException;import java.security.NoSuchProviderException;import java.security.SecureRandom;import java.security.spec.InvalidKeySpecException;public class AESUtils {        private static String aesEncyptPwd="ZIkun+KvXFWLZLYUwXqFWazQeRe119AkcGcl+p8Erzi4EEaHBFYcQuGuKthIE+1IWSQxoUpUJkT0T1+xtoRi3txDnBikdrFhccGZdRpqwRv58q5nqxJX4wVrq0Ms02KBKgQRTqqlzfYLzQcYPyhv8KPE8JDVkttic+W+j5pFles=";    private static final String KEY_ALGORITHM = "AES";    private static final String DEFAULT_CIPHER_ALGORITHM = "AES/ECB/PKCS5Padding";        public static String encrypt(String content) {        try {            Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM);            byte[] byteContent = content.getBytes("utf-8");            cipher.init(Cipher.ENCRYPT_MODE, getSecretKey(RSAUtils.decrypt(aesEncyptPwd)));            byte[] result = cipher.doFinal(byteContent);            return Base64Utils.encodeToString(result);        } catch (Exception ex) {            ex.printStackTrace();            System.out.println("AES加密失败");        }        return null;    }        public static String decrypt(String content) {        try {            //实例化            Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM);            //使用密钥初始化,设置为解密模式            cipher.init(Cipher.DECRYPT_MODE, getSecretKey(RSAUtils.decrypt(aesEncyptPwd)));            //执行操作            byte[] result = cipher.doFinal(Base64Utils.decodeFromString(content));            return new String(result, "utf-8");        } catch (Exception ex) {            ex.printStackTrace();            System.out.println("AES解密失败");        }        return null;    }        private static SecretKeySpec getSecretKey(String aesKey) {        //返回生成指定算法密钥生成器的 KeyGenerator 对象        KeyGenerator kg = null;        try {            kg = KeyGenerator.getInstance(KEY_ALGORITHM);            //AES 要求密钥长度为 128            SecureRandom random=SecureRandom.getInstance("SHA1PRNG","SUN");            random.setSeed(aesKey.getBytes());            kg.init(128, random);            //生成一个密钥            SecretKey secretKey = kg.generateKey();            return new SecretKeySpec(secretKey.getEncoded(), KEY_ALGORITHM);        } catch (NoSuchAlgorithmException ex) {            ex.printStackTrace();            System.out.println("生成加密秘钥失败");        } catch (NoSuchProviderException e) {            e.printStackTrace();            System.out.println("生成加密秘钥失败");        }        return null;    }}

生成license文件的方法

package com.license.tools.licensecreate.test;import com.license.tools.licensecreate.utils.AESUtils;import com.license.tools.licensecreate.utils.DateUtils;import org.dom4j.Document;import org.dom4j.DocumentHelper;import org.dom4j.Element;import org.dom4j.io.OutputFormat;import org.dom4j.io.XMLWriter;import java.io.File;import java.io.FileWriter;import java.util.Scanner;public class CreateSign {    public static void main(String[] args) {        Scanner sc = new Scanner(System.in);        //系统标识---由mac地址+cpu序列号,在客户工具端获取客户机mac地址和cpu序列号后用AES加密得到        System.out.println("请输入系统标识串(部署的服务获取):");        String systemSign = sc.nextLine();        System.out.println("请输入生效起始时间(格式如:2022-05-05 00:00:00):");        String generatedTimeStr = sc.nextLine();        System.out.println("请输入生效截止时间(格式如:2022-05-05 00:00:00):");        String expiredTimeStr = sc.nextLine();        System.out.println("请输入上一次校验时间初始值(格式如:2022-05-05 00:00:00):");        String lastValidateTimeStr = sc.nextLine();        System.out.println("请输入项目部署唯一版本号(不能带“-”):");        String version = sc.nextLine();        System.out.println("请输入license文件生成路径:");        String path = sc.nextLine();        createLicense(systemSign, generatedTimeStr, expiredTimeStr, lastValidateTimeStr, version, path);        System.out.println("license文件生成成功,文件路径:" + path);    }    private static void createLicense(String systemSign, String generatedTimeStr, String expiredTimeStr, String lastValidateTimeStr, String version, String path) {        try {            //解密系统标识得到mac地址+cpu序列号            String macAndCpu = AESUtils.decrypt(systemSign);            System.out.println("客户服务器mac地址和cpu序列号:" + macAndCpu);            //MAC地址-CPU序列号-生效起始时间-生效结束结束时间-软件产品序列号(项目版本唯一标识)//            String content = "A8:A1:59:41:89:36-BFEBFBFF000906EA-20220506-20220507-dmoiji3xkoa4p33";            StringBuilder signBuilder = new StringBuilder(macAndCpu);            //生效起始时间            long generatedTime = DateUtils.getTimeInMillis(generatedTimeStr);            //生效截止时间            long expiredTime = DateUtils.getTimeInMillis(expiredTimeStr);            //项目唯一标识            signBuilder.append("-").append(generatedTime).append("-").append(expiredTime).append("-").append(version);            String sign = AESUtils.encrypt(signBuilder.toString());            System.out.println("AES加密生成签名:");            System.out.println("-----------------------------------------------------------------------------------------------");            System.out.println(sign);            System.out.println("-----------------------------------------------------------------------------------------------");            //生成licence文件            Document document = DocumentHelper.createDocument();            //根节点            Element rootEle = document.addElement("license");            //功能数据节点,扩展参数时可在此节点下扩展            Element dataEle = rootEle.addElement("features");            Element featureEle = dataEle.addElement("feature");            featureEle.addAttribute("name", "lastValidateTi");            featureEle.addAttribute("ti", AESUtils.encrypt(String.valueOf(DateUtils.getTimeInMillis(lastValidateTimeStr))));            //签名节点            Element signEle = rootEle.addElement("signature");            signEle.setText(sign);            System.out.println(document.asXML());            OutputFormat format = OutputFormat.createPrettyPrint();            // 设置编码格式            format.setEncoding("UTF-8");            FileWriter fileWriter = new FileWriter(new File(path));            XMLWriter xmlWriter = new XMLWriter(fileWriter, format);            // 设置是否转义,默认使用转义字符            xmlWriter.setEscapeText(false);            xmlWriter.write(document);            xmlWriter.close();        } catch (Exception e) {            e.printStackTrace();        }    }}

其中第一步的系统标识由下面的客户工具获取客户机的mac地址和cpu序列号而来,然后将此方法生成的license文件和软件包同路径部署(也可修改客户工具中指定路径)

客户工具端:
只有解密方法的RSAUtils

package com.dtranx.tools.license.utils;import javax.crypto.BadPaddingException;import javax.crypto.Cipher;import javax.crypto.IllegalBlockSizeException;import javax.crypto.NoSuchPaddingException;import java.security.InvalidKeyException;import java.security.KeyFactory;import java.security.NoSuchAlgorithmException;import java.security.PrivateKey;import java.security.spec.InvalidKeySpecException;import java.security.spec.PKCS8EncodedKeySpec;import java.util.Base64;public class RSAUtils {        private static String prk = "MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAJ1+6OBOYbho5rigrZXKyYq8Aj6G6s3/C4pCPOAlOjdy3pMfk0cUedxv0peKMLXGTY+j6knxSNThcaNTiCrnKFN15Jg9hHqGPmOovLx+RakXN4LpG8zhssN7WUM0qmGlxSijlk7tPvvD19faWAlLLL/wvF0FOFzgx6CBrcpbX9bjAgMBAAECgYA8uRWohg//PdLXFHxY6JrUNrDW0sXtLoyQfgFimnfbsRpHt0DdgvOJHkQf0VP+gbqdyyEl6TWfflyGEErL39wX1rrosy+LpiN0HeISERJuwJtuiGeR+0qw+Xz2M7VE+e5oD94dRtlzERft2mcDbQAQYUCFNgUBtd1dCJgMJPZJYQJBANHxKKHqMbsH91JsGP8eCu+yeMah0X8cT79nwD71SJRc03W5P1MPKhRyGWJj0M+Wax32pAPCMTfbj19scLplJpUCQQDADD5OuSLYRVqx68/CYbFVK3ye/YD4Cgc+0kT9SoI9bLB10JumHT0seDGeXQqwUPAF3bBZGI8pW2bdtzDj8YGXAkABQXgEv+ncPIf2Lj9YB035cQ/X4E/oerrfYjd8KOtuN7/sDFecn5KY3LXaKM6u7y9k1nzUqOyycNXCtFtYQhKhAkBvgyxyvaFz/uFoyko6zksP705Pa1eFrx0B50pT4P26+O+FmXmnfPbWaXw2PkREmNqmLVGGinImS4JxXzuuP79FAkAFQejjE+5Twi8oSCcNwse7FFP86U6jgcc+S+XCUUkLXQ5SPlkyb037hwoV1lEEJpcyI2tSFRxBKT89KZN0Nfat";    protected static String decrypt(String signEncrypt) throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {        byte[] privateKeyBytes = prk.getBytes();        PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKeyBytes));        KeyFactory keyFactory = KeyFactory.getInstance("RSA");        PrivateKey priKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);        Cipher cipher = Cipher.getInstance("RSA");        cipher.init(Cipher.DECRYPT_MODE, priKey);        byte[] result = cipher.doFinal(Base64.getDecoder().decode(signEncrypt));        return new String(result);    }}

AESUtils

package com.dtranx.tools.license.utils;import org.springframework.util.Base64Utils;import javax.crypto.Cipher;import javax.crypto.KeyGenerator;import javax.crypto.SecretKey;import javax.crypto.spec.SecretKeySpec;import java.security.NoSuchAlgorithmException;import java.security.NoSuchProviderException;import java.security.SecureRandom;public class AESUtils {        private static String aesKey="ZIkun+KvXFWLZLYUwXqFWazQeRe119AkcGcl+p8Erzi4EEaHBFYcQuGuKthIE+1IWSQxoUpUJkT0T1+xtoRi3t" +            "xDnBikdrFhccGZdRpqwRv58q5nqxJX4wVrq0Ms02KBKgQRTqqlzfYLzQcYPyhv8KPE8JDVkttic+W+j5pFles=";    private static final String KEY_ALGORITHM = "AES";    private static final String DEFAULT_CIPHER_ALGORITHM = "AES/ECB/PKCS5Padding";        protected static String encrypt(String content) {        try {            Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM);            byte[] byteContent = content.getBytes("utf-8");            cipher.init(Cipher.ENCRYPT_MODE, getSecretKey(RSAUtils.decrypt(aesKey)));            byte[] result = cipher.doFinal(byteContent);            return Base64Utils.encodeToString(result);        } catch (Exception ex) {            ex.printStackTrace();            System.out.println("AES加密失败");        }        return null;    }        protected static String decrypt(String content) {        try {            //实例化            Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM);            //使用密钥初始化,设置为解密模式            cipher.init(Cipher.DECRYPT_MODE, getSecretKey(RSAUtils.decrypt(aesKey)));            //执行操作            byte[] result = cipher.doFinal(Base64Utils.decodeFromString(content));            return new String(result, "utf-8");        } catch (Exception ex) {            ex.printStackTrace();            System.out.println("AES解密失败");        }        return null;    }        private static SecretKeySpec getSecretKey(String aesKey) {        //返回生成指定算法密钥生成器的 KeyGenerator 对象        KeyGenerator kg = null;        try {            kg = KeyGenerator.getInstance(KEY_ALGORITHM);            //AES 要求密钥长度为 128            SecureRandom random=SecureRandom.getInstance("SHA1PRNG","SUN");            random.setSeed(aesKey.getBytes());            kg.init(128, random);            //生成一个密钥            SecretKey secretKey = kg.generateKey();            return new SecretKeySpec(secretKey.getEncoded(), KEY_ALGORITHM);        } catch (NoSuchAlgorithmException ex) {            ex.printStackTrace();            System.out.println("生成加密秘钥失败");        } catch (NoSuchProviderException e) {            e.printStackTrace();            System.out.println("生成加密秘钥失败");        }        return null;    }}

获取系统mac地址和cpu序列号的SystemUtils

package com.dtranx.tools.license.utils;import lombok.extern.slf4j.Slf4j;import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader;import java.net.InetAddress;import java.net.InterfaceAddress;import java.net.NetworkInterface;import java.util.ArrayList;import java.util.List;import java.util.Scanner;import java.util.stream.Collectors;@Slf4jpublic class Systemutils {    protected static String getMacAddress() {        try {            java.util.Enumeration en = NetworkInterface.getNetworkInterfaces();            StringBuilder sb = new StringBuilder();            while (en.hasMoreElements()) {                NetworkInterface iface = en.nextElement();                List addrs = iface.getInterfaceAddresses();                for (InterfaceAddress addr : addrs) {                    InetAddress ip = addr.getAddress();                    NetworkInterface network = NetworkInterface.getByInetAddress(ip);                    if (network == null) {                        continue;                    }                    if (network.getName().toLowerCase().startsWith("ens")) {                        byte[] mac = network.getHardwareAddress();                        if (mac == null) {continue;                        }                        for (int i = 0; i < mac.length; i++) {sb.append(String.format("%02X%s", mac[i], (i < mac.length - 1) ? "-" : ""));                        }                        String xxy = sb.toString().replaceAll("-", "").toUpperCase();                        log.info("xxy地址:{}", xxy);                        return xxy;                    }                }            }        } catch (Exception e) {            e.printStackTrace();            log.error("读取本机系统信息失败!");        }        return null;    }    protected static String getCpuNum() {        BufferedReader reader = null;        InputStreamReader ir = null;        try {            String[] linux = {"/bin/bash", "-c", "dmidecode -t processor | grep 'ID' | awk -F ':' '{print $2}' | head -n 1"};            String[] windows = {"wmic", "cpu", "get", "ProcessorId"};            // 获取系统信息            String property = System.getProperty("os.name");            Process process = Runtime.getRuntime().exec(property.contains("Window") ? windows : linux);            process.getOutputStream().close();            ir = new InputStreamReader(process.getInputStream());            reader = new BufferedReader(ir);            String xxw = reader.readLine();            if (xxw != null) {                xxw = xxw.replaceAll(" ", "");            }            log.info("xxw识别码:{}", xxw);            return xxw;        } catch (Exception e) {            e.printStackTrace();            log.error("获取系统信息失败!");        } finally {            if (reader != null) {                try {                    reader.close();                } catch (IOException e) {                    e.printStackTrace();                }            }            if (ir != null) {                try {                    ir.close();                } catch (IOException e) {                    e.printStackTrace();                }            }        }        return null;    }    public static void main(String[] args) throws Exception {//        List macs = getMacAddress();//        System.out.println("本机的mac网卡的地址列表" + macs);        System.out.println(getCpuNum());    }}

校验方法

package com.dtranx.tools.license.utils;import com.dtranx.tools.license.bean.CheckParams;import com.dtranx.tools.license.bean.ValidateCodeEnum;import com.dtranx.tools.license.bean.ValidateResult;import lombok.extern.slf4j.Slf4j;import org.dom4j.Document;import org.dom4j.Element;import org.dom4j.io.OutputFormat;import org.dom4j.io.SAXReader;import org.dom4j.io.XMLWriter;import org.springframework.stereotype.Component;import java.io.File;import java.io.FileWriter;import java.util.HashMap;import java.util.List;import java.util.Map;@Slf4j@Componentpublic class LicenseManager {    public static Map validate() {        Map result = new HashMap();        CheckParams checkParams = null;        try {            checkParams = getCheckParams(result);            if (checkParams == null) {                return result;            }        } catch (Exception e) {            e.printStackTrace();            result.put("Authorize", ValidateResult.error(ValidateCodeEnum.EXCEPTION));            return result;        }        //校验mac地址        if (!checkParams.getMacAddress().equals(Systemutils.getMacAddress())) {            result.put("Authorize", ValidateResult.error(ValidateCodeEnum.UNAUTHORIZED));            return result;        }        //校验cpu序列号        if (!checkParams.getCpuSerial().equals(Systemutils.getCpuNum())) {            result.put("Authorize", ValidateResult.error(ValidateCodeEnum.UNAUTHORIZED));            return result;        }        long currentTi = System.currentTimeMillis();        //校验时间        if (notAfterLastValidateTime(checkParams.getLastValidateTime(), currentTi) || notAfter(checkParams.getGeneratedTime(), currentTi)                || notBefore(checkParams.getExpiredTime(), currentTi)) {            result.put("Authorize", ValidateResult.error(ValidateCodeEnum.EXPIRED));            return result;        }        result.put("Authorize", ValidateResult.ok());        return result;    }    public static String getSystemSign() {        String MacAddress = Systemutils.getMacAddress();        String cpuNum = Systemutils.getCpuNum();        return AESUtils.encrypt(MacAddress + "-" + cpuNum);    }    public static void updateSign(String sign) {        try {            Document document = readLicense();            Element rootElement = document.getRootElement();            Element signatureEle = rootElement.element("signature");            signatureEle.setText(sign);            OutputFormat format = OutputFormat.createPrettyPrint();            // 设置编码格式            format.setEncoding("UTF-8");            String path = System.getProperty("user.dir");            FileWriter fileWriter = new FileWriter(new File(path + File.separator + "license.xml"));            XMLWriter xmlWriter = new XMLWriter(fileWriter, format);            // 设置是否转义,默认使用转义字符            xmlWriter.setEscapeText(false);            xmlWriter.write(document);            xmlWriter.close();            log.info("更新授权码成功");        } catch (Exception e) {            e.printStackTrace();            throw new RuntimeException("更新授权码失败!");        }    }    private static boolean notAfterLastValidateTime(long lastValidateTime, long currentTi) {        return lastValidateTime >= currentTi;    }    private static boolean notBefore(Long expiredTime, long currentTi) {        return expiredTime <= currentTi;    }    private static boolean notAfter(long generatedTime, long currentTi) {        return generatedTime >= currentTi;    }    private static CheckParams getCheckParams(Map result) {        //读取license文件        Document document = readLicense();        if (document == null) {            log.error("license 读取失败!");            result.put("Authorize", ValidateResult.error(ValidateCodeEnum.FILE_NOT_EXIST));            return null;        }        Element rootElement = document.getRootElement();        Element dataEle = rootElement.element("features");        List featuresEles = dataEle.elements();        Element lastValidateTimeEle = featuresEles.get(0);        //提取上一次验证时间        String lastValidateTimeStr = lastValidateTimeEle.attributeValue("ti");        long lastValidateTime = Long.parseLong(AESUtils.decrypt(lastValidateTimeStr));        log.debug("上一次校验时间:{}", lastValidateTime);        //提取签名内容        Element signEle = rootElement.element("signature");        String signStr = signEle.getText();        String sign = AESUtils.decrypt(signStr);        if (sign == null) {            log.error("授权码不正确");            result.put("Authorize", ValidateResult.error(ValidateCodeEnum.ILLEGAL));            return null;        }        log.debug("签名内容:{}", sign);        String[] signArr = sign.split("-");        if (signArr.length != 5) {            log.error("授权码不正确");            result.put("Authorize", ValidateResult.error(ValidateCodeEnum.ILLEGAL));            return null;        }        CheckParams params = CheckParams.builder().lastValidateTime(lastValidateTime).macAddress(signArr[0])                .cpuSerial(signArr[1]).generatedTime(Long.parseLong(signArr[2])).expiredTime(Long.parseLong(signArr[3]))                .version(signArr[4]).build();        return params;    }    private static Document readLicense() {        Document document = null;        try {            SAXReader saxReader = new SAXReader();            String path = System.getProperty("user.dir");            document = saxReader.read(new File(path + File.separator + "license.xml"));            return document;        } catch (Exception e) {            e.printStackTrace();        }        return null;    }    public static void main(String[] args) {//        String sign = AESUtils.decrypt("VorZodH/B6eeNLPA09TNJ8fpjlvrsckBk3VW3Pvr2qzhQVdeL38xS8unNFFxzQrjZ70f4wIoi1Tg1wlZq9DFKuVyp2zD20A//lDswyaD8NsmwMR72R2Ua+Gb0dp+PpM3b9gx2iIFIAtKOyaJlMMV8H4az/EKc/d733lyHfY3wbhsmo4vUvsqPYiriaj+psPu7DgO0DsQqw0xjAblpcrfL1xc42E3STEi9NTNbbBTsLU=");        String s="HPdW5CR3bRzVGEDMkZtsfQMHbcJ6SabTLJqdNsvJ7aU=";        System.out.println(AESUtils.decrypt(s));    }}

守护线程,定时获取校验结果,校验时间间隔从配置文件读取

package com.dtranx.tools.license.utils;import com.dtranx.tools.license.bean.ValidateResult;import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.annotation.Value;import org.springframework.context.annotation.Bean;import org.springframework.stereotype.Component;import java.util.Map;import java.util.concurrent.TimeUnit;@Component@Slf4jpublic class LicenseThread implements Runnable {    public static Map validateResult = null;    @Value("${xxy.checkTime}")    private Long checkTime;    @Bean    public void startThread() {        Thread thread = new Thread(this);        thread.setDaemon(true);        thread.start();    }    public void run() {        while (true) {            validateResult = LicenseManager.validate();            if (validateResult != null) {                ValidateResult result = validateResult.get("Authorize");                log.debug("license校验结果:" + result.getMessage());            }            try {                //正式改为12个小时校验一次,保持与登录同步即可//                TimeUnit.HOURS.sleep(12);                //测试1分钟校验一次                TimeUnit.SECONDS.sleep(checkTime);            } catch (InterruptedException e) {                e.printStackTrace();            }        }    }    public static boolean validateAfterUpdateSign() {        validateResult = LicenseManager.validate();        ValidateResult result = validateResult.get("Authorize");        return result != null && result.getIsValidate();    }}

获取cpu序列号时,实际是通过执行命令“dmidecode -t processor | grep ‘ID’ | awk -F ‘:’ ‘{print $2}’ | head -n 1”获取,在docker中运行服务,如果找不到dmidecode 命令,需要绑定硬件信息配置到容器内
docker 挂载目录增加

docker网络使用非宿主机网络时,docker内的MAC地址会随着docker的重启改变,导致之前生成的授权码校验不通过。处理措施有以下几种:
(1)docker容器内使用宿主机的网络“–net=host --privileged=true ”,则mac地址一直跟随宿主机
(2)docker启动命令添加指定mac地址“ --mac-address=xx:xx:xx:xx:xx:xx”

附完整源码地址(包含使用说明):github

来源地址:https://blog.csdn.net/black_dawn/article/details/129412297

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯