APDPlat提供了机器绑定的功能,对于不同的机器会生成不同的机器码,必须要对生成的机器码进行计算获得注册码,注册码验证成功之后系统才能正常运行。
本文主要介绍APDPlat如何生成机器码。
首先,定义了一个统一的接口,以支持不同操作系统不同实现的透明切换:
/** *生成机器码的接口,不同平台有不同实现 * @author 杨尚川 */ public interface SequenceService { /** * 获取机器码 * @return 机器码 */ public String getSequence(); }
其次,定义了一个抽象类,实现了一些通用的功能,如生成MD5摘要、将很长的字符串以固定的位数分割开,以便于人类阅读(如将机器码 71F5DA7F495E7F706D47F3E63DC6349A 每4个一组,以-分割为71F5-DA7F-495E-7F70-6D47-F3E6-3DC6-349A)、利用sigar来生成机器码。这里需要在pom.xml中加入sigar依赖。
<dependency> <groupId>org.fusesource</groupId> <artifactId>sigar</artifactId> <version>1.6.4</version> <exclusions> <exclusion> <groupId>log4j</groupId> <artifactId>log4j</artifactId> </exclusion> </exclusions> </dependency>
import org.apdplat.module.system.service.PropertyHolder; import org.apdplat.platform.log.APDPlatLogger; import org.apdplat.platform.util.ConvertUtils; import org.apdplat.platform.util.FileUtils; import java.io.File; import java.io.UnsupportedEncodingException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.HashSet; import java.util.Properties; import java.util.Set; import org.apdplat.platform.log.APDPlatLoggerFactory; import org.hyperic.sigar.Mem; import org.hyperic.sigar.NetFlags; import org.hyperic.sigar.NetInterfaceConfig; import org.hyperic.sigar.Sigar; /** *机器码生成的通用服务 * @author 杨尚川 */ public abstract class AbstractSequenceService implements SequenceService{ protected final APDPlatLogger LOG = APDPlatLoggerFactory.getAPDPlatLogger(getClass()); /** * 对一段String生成MD5摘要信息 * @param message 要摘要的String * @return 生成的MD5摘要信息 */ protected String getMD5(String message) { message += "{apdplat}"; try { MessageDigest md = MessageDigest.getInstance("MD5"); LOG.debug("MD5摘要长度:" + md.getDigestLength()); byte[] b = md.digest(message.getBytes("utf-8")); String md5 = ConvertUtils.byte2HexString(b)+message.length(); return getSplitString(md5); } catch (NoSuchAlgorithmException | UnsupportedEncodingException e) { LOG.error("MD5摘要失败",e); } return null; } /** * 将很长的字符串以固定的位数分割开,以便于人类阅读 * @param str * @return */ protected String getSplitString(String str){ return getSplitString(str, "-", 4); } /** * 将很长的字符串以固定的位数分割开,以便于人类阅读 * 如将 * 71F5DA7F495E7F706D47F3E63DC6349A * 以-,每4个一组,则分割为 * 71F5-DA7F-495E-7F70-6D47-F3E6-3DC6-349A * @param str 字符串 * @param split 分隔符 * @param length 长度 * @return */ protected String getSplitString(String str, String split, int length){ int len=str.length(); StringBuilder temp=new StringBuilder(); for(int i=0;i<len;i++){ if(i%length==0 && i>0){ temp.append(split); } temp.append(str.charAt(i)); } String[] attrs=temp.toString().split(split); StringBuilder finalMachineCode=new StringBuilder(); for(String attr : attrs){ if(attr.length()==length){ finalMachineCode.append(attr).append(split); } } String result=finalMachineCode.toString().substring(0, finalMachineCode.toString().length()-1); return result; } /** * 利用sigar来生成机器码,当然这个实现不是很好,无法获得CPU ID,希望有兴趣的朋友来改进这个实现 * @param osName 操作系统类型 * @return 机器码 */ protected String getSigarSequence(String osName) { try { File libFile = new File(FileUtils.getAbsolutePath("/WEB-INF/lib/"+PropertyHolder.getProperty("libsigar."+osName))); LOG.debug("libsigar."+osName+" : "+libFile.getAbsolutePath()); System.load(libFile.getAbsolutePath()); Set<String> result = new HashSet<>(); Sigar sigar = new Sigar(); String[] ifaces = sigar.getNetInterfaceList(); for (String iface : ifaces) { NetInterfaceConfig cfg = sigar.getNetInterfaceConfig(iface); if (NetFlags.LOOPBACK_ADDRESS.equals(cfg.getAddress()) || (cfg.getFlags() & NetFlags.IFF_LOOPBACK) != 0 || NetFlags.NULL_HWADDR.equals(cfg.getHwaddr())) { continue; } String mac = cfg.getHwaddr(); result.add(mac); LOG.debug("mac: " + mac); } if(result.size()<1){ return null; } Properties props = System.getProperties(); String javaVersion = props.getProperty("java.version"); result.add(javaVersion); LOG.debug("Java的运行环境版本: " + javaVersion); String javaVMVersion = props.getProperty("java.vm.version"); result.add(javaVMVersion); LOG.debug("Java的虚拟机实现版本: " + props.getProperty("java.vm.version")); String osVersion = props.getProperty("os.version"); result.add(osVersion); LOG.debug("操作系统的版本: " + props.getProperty("os.version")); Mem mem = sigar.getMem(); // 内存总量 String totalMem = mem.getTotal() / 1024L + "K av"; LOG.debug("内存总量: " + totalMem); result.add(totalMem); LOG.debug("result: " + result); String machineCode = getMD5(result.toString()); return machineCode; } catch (Throwable ex) { LOG.error("生成 "+osName+" 平台下的机器码失败", ex); } return null; } }
最后,我们分别看看不同平台的实现。
Windows:
/** *在Windows平台上生成机器码 * @author 杨尚川 */ public final class WindowsSequenceService extends AbstractSequenceService{ @Override public String getSequence() { String cpuID=getCPUSerial(); String hdID=getHDSerial("C"); if(cpuID==null || hdID==null){ return null; } String machineCode = getMD5(cpuID+hdID); return machineCode; } /** * * @param drive 硬盘驱动器分区 如C,D * @return 该分区的卷标 */ private String getHDSerial(String drive) { StringBuilder result = new StringBuilder(); try { File file = File.createTempFile("tmp", ".vbs"); file.deleteOnExit(); try (FileWriter fw = new java.io.FileWriter(file)) { String vbs = "Set objFSO = CreateObject(\"Scripting.FileSystemObject\")\n" + "Set colDrives = objFSO.Drives\n" + "Set objDrive = colDrives.item(\"" + drive + "\")\n" + "Wscript.Echo objDrive.SerialNumber"; fw.write(vbs); } Process p = Runtime.getRuntime().exec("cscript //NoLogo " + file.getPath()); try (BufferedReader input = new BufferedReader(new InputStreamReader(p.getInputStream()))) { String line; while ((line = input.readLine()) != null) { result.append(line); } } file.delete(); } catch (Throwable e) { LOG.error("生成HDSerial失败", e); } if (result.length() < 1) { LOG.info("无磁盘ID被读取"); } return result.toString(); } /** * 获取CPU号,多CPU时,只取第一个 * @return */ private String getCPUSerial() { StringBuilder result = new StringBuilder(); try { File file = File.createTempFile("tmp", ".vbs"); file.deleteOnExit(); try (FileWriter fw = new FileWriter(file)) { String vbs = "On Error Resume Next \r\n\r\n" + "strComputer = \".\" \r\n" + "Set objWMIService = GetObject(\"winmgmts:\" _ \r\n" + " & \"{impersonationLevel=impersonate}!\\\\\" & strComputer & \"\\root\\cimv2\") \r\n" + "Set colItems = objWMIService.ExecQuery(\"Select * from Win32_Processor\") \r\n " + "For Each objItem in colItems\r\n " + " Wscript.Echo objItem.ProcessorId \r\n " + " exit for ' do the first cpu only! \r\n" + "Next "; fw.write(vbs); } Process p = Runtime.getRuntime().exec("cscript //NoLogo " + file.getPath()); try (BufferedReader input = new BufferedReader(new InputStreamReader(p.getInputStream()))) { String line; while ((line = input.readLine()) != null) { result.append(line); } } file.delete(); } catch (Throwable e) { LOG.error("生成CPUSerial失败", e); } if (result.length() < 1) { LOG.info("无CPU_ID被读取"); } return result.toString(); } public static void main(String[] args) { SequenceService s = new WindowsSequenceService(); String seq = s.getSequence(); System.out.println(seq); } }
Linux:
/** *在Linux平台上生成机器码 * @author 杨尚川 */ public class LinuxSequenceService extends AbstractSequenceService{ @Override public String getSequence() { return getSigarSequence("linux"); } public static void main(String[] args) { SequenceService s = new LinuxSequenceService(); String seq = s.getSequence(); System.out.println(seq); } }
Mac OS X:
/** *在Mac OS X平台上生成机器码 * @author 杨尚川 */ public class MacSequenceService extends AbstractSequenceService{ @Override public String getSequence() { return getSigarSequence("mac"); } public static void main(String[] args) { SequenceService s = new MacSequenceService(); String seq = s.getSequence(); System.out.println(seq); } }
Solaris:
/** *在Solaris平台上生成机器码 * @author 杨尚川 */ public class SolarisSequenceService extends AbstractSequenceService{ @Override public String getSequence() { return getSigarSequence("solaris"); } public static void main(String[] args) { SequenceService s = new SolarisSequenceService(); String seq = s.getSequence(); System.out.println(seq); } }
APDPlat托管在Github