系统授权试用,限制机器和使用日期

本文介绍了如何使用Java编写一个授权工具,通过AES加密保护授权文件,包括获取服务器物理地址、配置授权信息、授权文件的生成与验证过程,以及使用定时任务确保授权的持续有效性。
摘要由CSDN通过智能技术生成

授权工具下载地址(不会上传文件,可以私信问我要)https://download.csdn.net/download/m0_57478867/88782028?spm=1001.2014.3001.5503

使用授权工具生成授权文件

Window+R---->cmd输入 ipconfig /all  获取当前服务器的物理地址

双击授权工具

弹出授权窗口

选择授权时间

输入需要授权的物理地址

生成授权文件

授权代码

配置文件

application.yml配置授权文件路径

jeecg:
  authMachine:
    path: license.key

/**
 * 加载项目配置
 */
@Component("jeecgBaseConfig")
@ConfigurationProperties(prefix = "jeecg")
public class JeecgBaseConfig {
/**
     * 授权机器
     */
    private AuthMachine authMachine;
    public AuthMachine getAuthMachine() {
        return authMachine;
    }

    public void setAuthMachine(AuthMachine authMachine) {
        this.authMachine = authMachine;
    }
}

授权拦截器


import lombok.extern.slf4j.Slf4j;
import org.jeecg.common.util.IpUtils;
import org.jeecg.common.util.encryption.AesEncryptUtil;
import org.jeecg.config.JeecgBaseConfig;
import org.jeecg.config.vo.AuthMachine;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;

/**
 * 授权拦截器
 * @author xxx
 * @data 0000/0/0 
 * info:
 */
@Slf4j
public class AuthInterceptor implements HandlerInterceptor {
    @Resource
    JeecgBaseConfig jeecgBaseConfig;
    Long validTime;
    boolean validFlag=true;
    Timer timer=null;
    /**
     * 将内容回写到文件中
     *
     * @param filePath
     * @param content
     */
    public void write(String filePath, String content) {
        BufferedWriter bw = null;

        try {
            // 根据文件路径创建缓冲输出流
            bw = new BufferedWriter(new FileWriter(filePath));
            // 将内容写入文件中
            bw.write(content);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 关闭流
            if (bw != null) {
                try {
                    bw.close();
                } catch (IOException e) {
                    bw = null;
                }
            }
        }
    }
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        if(!validFlag)
            return false;//超出有效期
        long nowTime = System.currentTimeMillis();//获取当前时间
        //获取配置信息
        AuthMachine authMachine = jeecgBaseConfig.getAuthMachine();
        String filePath = authMachine.getPath();
        //判断是否存在license.key文件
        File file=new File(filePath);
        if(!file.exists()){//初始化授权
            log.error("未识别到授权文件!");
            return false;
        }
        else{
            if(timer==null) {//如果定时关闭,重新启动定时
                boolean vflag=vaildFile();
                if(vflag)
                    startTimerMonitor();//重启定时
                else
                    return false;
            }
            else{//系统时间被修改,timer会被挂起
                String str = null;
                int i = 0;
                StringBuffer buf = new StringBuffer();
                FileReader fileReader = new FileReader(filePath);
                BufferedReader bufferedReader = new BufferedReader(fileReader);
                while ((str = bufferedReader.readLine()) != null) {
                    if (i == 3) {
                        String s = AesEncryptUtil.desEncrypt(str);//解密
                        Long time = Long.valueOf(s);
                        if (nowTime < time) {
                            log.info("当前时间:"+ System.currentTimeMillis());
                            log.error("签名验证失败:授权时间异常!");
                            return false;
                        }
                    }
                    i++;
                }
            }
        }

        return true;
    }
    //验证授权文件
    private boolean vaildFile(){
        try {
            //获取当前服务器的所有ip的物理地址
            List<String> curMacs =IpUtils.getMacs();
            long nowTime = System.currentTimeMillis();//获取当前时间
            String nowTimeAes = null;
            nowTimeAes = AesEncryptUtil.encrypt(String.valueOf(nowTime));
            //获取配置信息
            AuthMachine authMachine = jeecgBaseConfig.getAuthMachine();
            String validStartTime = null;
            String validEndTime = null;
            String filePath = authMachine.getPath();
            //读取第一行的机器码
            FileReader fileReader = new FileReader(filePath);
            BufferedReader bufferedReader = new BufferedReader(fileReader);
            String str = null;
            int i = 0;
            String licenseCode = null;
            StringBuffer buf = new StringBuffer();
            while ((str = bufferedReader.readLine()) != null) {
                if (i == 0) {
                    licenseCode = str;
                    buf.append(str).append("\n");
                } else if (i == 1) {
                    validStartTime = str;
                    buf.append(str).append("\n");
                } else if (i == 2) {
                    validEndTime = str;
                    buf.append(str).append("\n");
                } else if (i == 3) {
                    String s = AesEncryptUtil.desEncrypt(str);//解密
                    Long time = Long.valueOf(s);
                    if (nowTime < time) {
                        log.error("签名验证失败:授权时间异常!"+"当前时间:"+ nowTime+",最新调用时间:"+time);
                        return false;
                    }
                    buf.append(nowTimeAes);//更新时间
                }
                i++;
            }
            String code=AesEncryptUtil.desEncrypt(licenseCode);
            if (!curMacs.contains(code)) {//如果集合里不包含
                log.error("签名验证失败:机器码与license不匹配!");
                return false;
            }
            //验证授权日期
            String validSt = AesEncryptUtil.desEncrypt(validStartTime);
            String validEt = AesEncryptUtil.desEncrypt(validEndTime);
            long sTime = validSt != null ? Long.parseLong(validSt) : 0;
            long eTime = validEt != null ? Long.parseLong(validEt) : 0;
            if (validTime == null) {
                if (sTime < nowTime)
                    validTime = eTime - nowTime;//计算有效时长
                else
                    validTime = eTime - sTime;
            }

            if (sTime > nowTime || eTime < nowTime) {
                log.info("当前时间:"+ System.currentTimeMillis());
                log.error("签名验证失败:授权已过期!");
                return false;
            }
            write(filePath, buf.toString()); // 读取修改文件
            return true;
        }catch (Exception e) {
            log.error(e.getMessage());
            return false;
        }
    }
    //启动定时
    private void startTimerMonitor() {
        // 延时时间
        long delayTime =1000 * 60*10;// 1000 * 60*60;
        // 执行频率
        long period = 1000 * 60*10;//1000 * 60*60;
        TimerTask timerTask = new TimerTask() {//每10分钟执行一次
            @Override
            public void run() {
                validFlag=vaildFile();
                validTime=validTime-period;//3600000;
                Long MM=validTime/6000;
                log.info("当前时间:"+ System.currentTimeMillis()+",距离授权还剩:"+MM+"分");
                if(validTime<=0) {
                    System.out.println("授权已到期!!");
                    cancel();
                }

            }
        };
        // 定时器
        timer = new Timer();
        timer.scheduleAtFixedRate(timerTask,delayTime,period);
    }
}

实现原理:读取授权文件的时间和机器码并解密,验证是否一致

      加个定时器去更新当前时间并持续写入到授权文件里(避免修改电脑时间或关机等操作跳过授权)

AES加密工具类

import org.apache.shiro.codec.Base64;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

/**
 * @Description: AES 加密
 * @author: xxx
 * @date: 0000/0/0 
 */
public class AesEncryptUtil {

    /**
     * 使用AES-128-CBC加密模式 key和iv可以相同
     */
    private static String KEY = EncryptedString.key;
    private static String IV = EncryptedString.iv;

    /**
     * 加密方法
     * @param data  要加密的数据
     * @param key 加密key
     * @param iv 加密iv
     * @return 加密的结果
     * @throws Exception
     */
    public static String encrypt(String data, String key, String iv) throws Exception {
        try {

            //"算法/模式/补码方式"NoPadding PkcsPadding
            Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
            int blockSize = cipher.getBlockSize();

            byte[] dataBytes = data.getBytes();
            int plaintextLength = dataBytes.length;
            if (plaintextLength % blockSize != 0) {
                plaintextLength = plaintextLength + (blockSize - (plaintextLength % blockSize));
            }

            byte[] plaintext = new byte[plaintextLength];
            System.arraycopy(dataBytes, 0, plaintext, 0, dataBytes.length);

            SecretKeySpec keyspec = new SecretKeySpec(key.getBytes(), "AES");
            IvParameterSpec ivspec = new IvParameterSpec(iv.getBytes());

            cipher.init(Cipher.ENCRYPT_MODE, keyspec, ivspec);
            byte[] encrypted = cipher.doFinal(plaintext);

            return Base64.encodeToString(encrypted);

        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 解密方法
     * @param data 要解密的数据
     * @param key  解密key
     * @param iv 解密iv
     * @return 解密的结果
     * @throws Exception
     */
    public static String desEncrypt(String data, String key, String iv) throws Exception {
        //update-begin-author:taoyan date:2022-5-23 for:VUEN-1084 【vue3】online表单测试发现的新问题 6、解密报错 ---解码失败应该把异常抛出去,在外面处理
        byte[] encrypted1 = Base64.decode(data);

        Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
        SecretKeySpec keyspec = new SecretKeySpec(key.getBytes(), "AES");
        IvParameterSpec ivspec = new IvParameterSpec(iv.getBytes());

        cipher.init(Cipher.DECRYPT_MODE, keyspec, ivspec);

        byte[] original = cipher.doFinal(encrypted1);
        String originalString = new String(original);
        //加密解码后的字符串会出现\u0000
        return originalString.replaceAll("\\u0000", "");
        //update-end-author:taoyan date:2022-5-23 for:VUEN-1084 【vue3】online表单测试发现的新问题 6、解密报错 ---解码失败应该把异常抛出去,在外面处理
    }

    /**
     * 使用默认的key和iv加密
     * @param data
     * @return
     * @throws Exception
     */
    public static String encrypt(String data) throws Exception {
        return encrypt(data, KEY, IV);
    }

    /**
     * 使用默认的key和iv解密
     * @param data
     * @return
     * @throws Exception
     */
    public static String desEncrypt(String data) throws Exception {
        return desEncrypt(data, KEY, IV);
    }

}

AES密钥和偏移量配置

/**
 * @Description: EncryptedString
 * @author: xxxx
 */
@Data
public class  EncryptedString {

    /**
     * 长度为16个字符
     */
    public static  String key = "1234567890adbcde";

    /**
     * 长度为16个字符
     */
    public static  String iv  = "1234567890hjlkew";
}

  • 10
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
以下是一个简单的 C++ 代码示例,用于限制软件试用期,并防止用户通过篡改系统时间来绕过限制: ```c++ #include <iostream> #include <ctime> using namespace std; int main() { // 定义试用期为 30 天 const int TRIAL_PERIOD = 30; // 获取当前系统时间 time_t now = time(0); // 将当前时间转换为 tm 结构体 tm* currentTime = localtime(&now); // 将当前时间转换为秒数 int currentSeconds = currentTime->tm_mday * 24 * 60 * 60 + currentTime->tm_hour * 60 * 60 + currentTime->tm_min * 60 + currentTime->tm_sec; // 获取程序首次运行时间 int firstRunTime = 0; ifstream inFile("time.txt"); if (!inFile) { // 如果首次运行,将当前时间写入到文件中 firstRunTime = currentSeconds; ofstream outFile("time.txt"); if (outFile) { outFile << firstRunTime; outFile.close(); } } else { // 如果不是首次运行,从文件中读取首次运行时间 inFile >> firstRunTime; inFile.close(); } // 计算已经运行的天数 int days = (currentSeconds - firstRunTime) / (24 * 60 * 60); // 判断是否超过试用期 if (days >= TRIAL_PERIOD) { cout << "试用期已过,请购买正版软件!" << endl; return 0; } else { cout << "欢迎使用本软件,您还有 " << TRIAL_PERIOD - days << " 天试用期!" << endl; } return 0; } ``` 这里我们使用了一个名为 `time.txt` 的文件来保存程序首次运行的时间。程序首次运行时,会将当前时间写入到该文件中;以后每次运行时,会从该文件中读取首次运行时间,并计算已经运行的天数。如果已经超过试用期,则提示用户购买正版软件。这种方法可以防止用户通过篡改系统时间来绕过试用限制
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

姗姗的鱼尾喵

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值