2021-10-19

本文详细介绍了如何在系统级框架中实现授权认证机制。首先,通过获取MAC地址和其他系统参数生成机器码,并进行MD5加密。接着,利用RSA加密生成包含机器码、有效期等信息的lic文件。在系统中,当用户上传lic文件后,系统会通过内置公钥对文件内容进行解密验证,确保授权合法。如果验证失败,用户将无法正常使用系统。

在我们做系统级框架的时候,我们要一定程度上考虑系统的使用版权,不能随便一个人拿去在任何环境都能用,所以我们需要给我们系统做一个授权认证机制,只有上传了我们下发的lic文件并验证通过,才能正常使用,下面就开始一步一步实现这个功能

1.生成机器码

我们首先要做的就是对软件部署的环境的唯一性进行限制,这里使用的是macadderss,当然你也可以换成cpu序列编号,并无太大影响,先上代码


   
  1. private static String getMac () {
  2. try {
  3. Enumeration<NetworkInterface> el = NetworkInterface
  4. .getNetworkInterfaces();
  5. while (el.hasMoreElements()) {
  6. byte[] mac = el.nextElement().getHardwareAddress();
  7. if (mac == null)
  8. continue;
  9. String hexstr = bytesToHexString(mac);
  10. return getSplitString(hexstr, "-", 2).toUpperCase();
  11. }
  12. } catch (Exception exception) {
  13. exception.printStackTrace();
  14. }
  15. return null;
  16. }
  17. public static String getMachineCode () throws Exception{
  18. Set<String> result = new HashSet<>();
  19. String mac = getMac();
  20. result.add(mac);
  21. Properties props = System.getProperties();
  22. String javaVersion = props.getProperty( "java.version");
  23. result.add(javaVersion);
  24. String javaVMVersion = props.getProperty( "java.vm.version");
  25. result.add(javaVMVersion);
  26. String osVersion = props.getProperty( "os.version");
  27. result.add(osVersion);
  28. String code = Encrpt.GetMD5Code(result.toString());
  29. return getSplitString(code, "-", 4);
  30. }

这里进行的操作是取出机器码,与java版本,jvm,操作系统参数进行混合,并进行MD5操作

2.进行lic文件的生成

这是我生成证书与进行授权证书的界面,可以看到授权证书主要包含三个要素,机器码,是否永久有效标识,证书时效,我们会将这些数据写入文本中并进行加密处理,看下生成证书的代码


   
  1. public static void getLicense (String isNoTimeLimit, String licenseLimit, String machineCode, String licensePath, String priavateKeyPath) throws Exception{
  2. String[] liccontent = {
  3. "LICENSEID=yanpeng19940119@gmail.com",
  4. "LICENSENAME=YBLOG使用证书",
  5. MessageFormat.format( "LICENSETYPE={0}",isNoTimeLimit),
  6. MessageFormat.format( "EXPIREDAY={0}",licenseLimit), //日期采用yyyy-MM-dd日期格式
  7. MessageFormat.format( "MACHINECODE={0}",machineCode),
  8. ""
  9. };
  10. //将lic内容进行混合签名并写入内容
  11. StringBuilder sign = new StringBuilder();
  12. for(String item:liccontent){
  13. sign.append(item+ "yblog");
  14. }
  15. liccontent[ 5] = MessageFormat.format( "LICENSESIGN={0}",Encrpt.GetMD5Code(sign.toString()));
  16. FileUtil.createFileAndWriteLines(licensePath,liccontent);
  17. //将写入的内容整体加密替换
  18. String filecontent =FileUtil.readFileToString(licensePath);
  19. String encrptfilecontent = Encrpt.EncriptWRSA_Pri(filecontent,priavateKeyPath);
  20. File file = new File(licensePath);
  21. file.delete();
  22. FileUtil.createFile(licensePath,encrptfilecontent);
  23. }

这里我们是将一些信息与特定标识进行拼接然后加密,使用的是RSA加密,我们使用私钥加密公钥解密,保证验证的开放性与生成证书的私密性,密钥可以使用java自带的keytool工具进行生成,教程地址:http://note.youdao.com/noteshare?id=09e2bfc902b21a335a4505f7946a45c9,在lic文件最后我们加上一个LICENSESIGN参数,对其他信息进行一次加密,防止信息被篡改,生成文件后再对文本进行整体加密

这里生成密钥的长度为2048而非1024,所以解密块长度为256,这里需要注意下,公钥加密方法为,为了方便大家,这里提供下具体加密代码


   
  1. private static final int MAX_ENCRYPT_BLOCK = 117;
  2. private static final int MAX_DECRYPT_BLOCK= 256;
  3. public static String EncriptWRSA_Pri (String data,String path) throws Exception{
  4. String encryptData = "";
  5. FileInputStream in = new FileInputStream(path);
  6. KeyStore ks = KeyStore.getInstance( "JKS"); // JKS: Java KeyStoreJKS,可以有多种类型
  7. ks.load(in, "123".toCharArray());
  8. in.close();
  9. String alias = "yblogkey"; // 记录的别名
  10. String pswd = "123"; // 记录的访问密码
  11. java.security.cert. Certificate cert = ks.getCertificate(alias);
  12. //获取私钥
  13. PrivateKey privateKey = (PrivateKey) ks.getKey(alias, pswd.toCharArray());
  14. //私钥加密
  15. Cipher cipher = Cipher.getInstance( "rsa");
  16. SecureRandom random = new SecureRandom();
  17. cipher.init(Cipher.ENCRYPT_MODE, privateKey, random);
  18. try {
  19. // Cipher cipher = Cipher.getInstance("RSA");
  20. // cipher.init(Cipher.ENCRYPT_MODE, publicKey);
  21. int length = data.getBytes().length;
  22. int offset = 0;
  23. byte[] cache;
  24. ByteArrayOutputStream outStream = new ByteArrayOutputStream();
  25. int i = 0;
  26. while(length - offset > 0){
  27. if(length - offset > MAX_ENCRYPT_BLOCK){
  28. cache = cipher.doFinal(data.getBytes(), offset, MAX_ENCRYPT_BLOCK);
  29. } else{
  30. cache = cipher.doFinal(data.getBytes(), offset, length - offset);
  31. }
  32. outStream.write(cache, 0, cache.length);
  33. i++;
  34. offset = i * MAX_ENCRYPT_BLOCK;
  35. }
  36. return encode.encode(outStream.toByteArray());
  37. } catch (IllegalBlockSizeException e) {
  38. e.printStackTrace();
  39. } catch (BadPaddingException e) {
  40. e.printStackTrace();
  41. }
  42. return encryptData;
  43. }
  44. public static String DecriptWithRSA_Pub (String data,String path) throws Exception{
  45. X509Certificate x509Certificate = (X509Certificate) getCertificate(path);
  46. // 获得公钥
  47. PublicKey publicKey = x509Certificate.getPublicKey();
  48. Cipher cipher = Cipher.getInstance( "rsa");
  49. SecureRandom random = new SecureRandom();
  50. byte[] bEncrypt = decoder.decodeBuffer(data);
  51. //公钥解密
  52. cipher.init(Cipher.DECRYPT_MODE, publicKey, random);
  53. String decryptData = "";
  54. // byte[] plainData = cipher.doFinal(bEncrypt);
  55. // System.out.println("11111:"+new String(plainData));
  56. int inputLen = bEncrypt.length;
  57. ByteArrayOutputStream out = new ByteArrayOutputStream();
  58. int offSet = 0;
  59. byte[] cache;
  60. int i = 0;
  61. // 对数据分段解密
  62. while (inputLen - offSet > 0) {
  63. if (inputLen - offSet > MAX_DECRYPT_BLOCK) {
  64. cache = cipher.doFinal(bEncrypt, offSet, MAX_DECRYPT_BLOCK);
  65. } else {
  66. cache = cipher.doFinal(bEncrypt, offSet, inputLen - offSet);
  67. }
  68. out.write(cache, 0, cache.length);
  69. i++;
  70. offSet = i * MAX_DECRYPT_BLOCK;
  71. }
  72. byte[] decryptedData = out.toByteArray();
  73. out.close();
  74. return new String(decryptedData);
  75. }

3.验证lic

我们会在系统中注册一个拦截器,未通过系统授权认证会自动跳转到lic文件上传界面,springboot接收文件与常规java有一些不同,使用的MultipartFile对象,会获取到上传文件的数组,进行操作,看下保存上传lic文件代码


   
  1. @RequestMapping(value= "/login/licenseauth",method= RequestMethod. POST)
  2. @ResponseBody
  3. public Map< Object, Object> licenseauth( MultipartHttpServletRequest multiReq){
  4. Map< Object, Object> map = new HashMap< Object, Object>();
  5. try {
  6. String savePath = ResourceUtils. getURL( "src/main/resources/static/lic"). getPath();
  7. MultipartFile file = multiReq. getFile( "file");
  8. String filename = file. getOriginalFilename();
  9. File uploadfile = new File(savePath + "\\" + filename);
  10. if (!uploadfile. exists()){
  11. //获取item中的上传文件的输入流
  12. InputStream in = file. getInputStream();
  13. //创建一个文件输出流
  14. FileOutputStream out = new FileOutputStream(savePath + "\\" + filename);
  15. //创建一个缓冲区
  16. byte buffer[] = new byte[ 1024];
  17. //判断输入流中的数据是否已经读完的标识
  18. int len = 0;
  19. //循环将输入流读入到缓冲区当中,(len=in.read(buffer))>0就表示in里面还有数据
  20. while((len= in. read(buffer))> 0){
  21. //使用FileOutputStream输出流将缓冲区的数据写入到指定的目录(savePath + "\\" + filename)当中
  22. out. write(buffer, 0, len);
  23. }
  24. //关闭输入流
  25. in. close();
  26. //关闭输出流
  27. out. close();
  28. }
  29. map. put( "executestatus", "1");
  30. } catch ( Exception e){
  31. e. printStackTrace();
  32. map. put( "executestatus", "0");
  33. }
  34. return map;
  35. }

有了上传文件,我们就可以通过系统内置的公钥对lic文件的机器码,授权时间进行验证,确定是否能正常访问系统


   
  1. public static boolean authLicense () throws Exception{
  2. boolean isauth = false;
  3. String pubkpath = ResourceUtils.getURL( "src/main/resources/static/lic/").getPath()+ "yblog.crt";
  4. String licpath = ResourceUtils.getURL( "src/main/resources/static/lic/").getPath();
  5. File lic = new File(licpath);
  6. String[] filelist = lic.list();
  7. if (filelist.length> 0){
  8. for ( int i = 0; i < filelist.length; i++) {
  9. if (filelist[i].contains( ".lic")){
  10. File readfile = new File(licpath + filelist[i]);
  11. if (readfile.isFile()) {
  12. String liccontent = FileUtil.readFileToString(readfile);
  13. String decriptliccontent = Encrpt.DecriptWithRSA_Pub(liccontent,pubkpath);
  14. HashMap<String, String> props = genDataFromArrayByte(decriptliccontent.getBytes());
  15. String licenseid = props.get( "LICENSEID");
  16. String licensename= props.get( "LICENSENAME");
  17. String licensetype = props.get( "LICENSETYPE");
  18. String liclimit = props.get( "EXPIREDAY");
  19. String machinecode = props.get( "MACHINECODE");
  20. String lincensesign = props.get( "LICENSESIGN");
  21. //验证签名
  22. String allinfogroup = "LICENSEID="+licenseid+ "yblog"+ "LICENSENAME="+licensename+ "yblog"+
  23. "LICENSETYPE="+licensetype+ "yblog"+ "EXPIREDAY="+liclimit+ "yblog"+ "MACHINECODE="+machinecode+ "yblogyblog";
  24. if (lincensesign.equals(Encrpt.GetMD5Code(allinfogroup))){
  25. //验证机器码
  26. if (getMachineCode().equals(machinecode)){
  27. SimpleDateFormat sdf= new SimpleDateFormat( "yyyy-MM-dd");
  28. Date bt= new Date();
  29. Date et=sdf.parse(liclimit);
  30. //验证时间
  31. if(bt.compareTo(et)<= 0){
  32. isauth = true;
  33. System.out.println( "注册文件:"+filelist[i]+ ",已通过验证");
  34. break;
  35. } else{
  36. System.out.println( "证书过期");
  37. }
  38. } else{
  39. System.out.println( "机器码不一致");
  40. }
  41. } else{
  42. System.out.println( "签名不一致");
  43. }
  44. }
  45. }
  46. }
  47. } else{
  48. System.out.println( "未上传证书");
  49. }
  50. return isauth;
  51. }

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值