Springboot如何實現Web系統License授權認證
在我們做系統級框架的時候,我們要一定程度上考慮系統的使用版權,不能隨便一個人拿去在任何環境都能用,所以我們需要給我們系統做一個授權認證機制,只有上傳了我們下發的lic檔案並驗證通過,才能正常使用,下面就開始一步一步實現這個功能
1.生成機器碼
我們首先要做的就是對軟體部署的環境的唯一性進行限制,這裡使用的是macadderss,當然你也可以換成cpu序列編號,並無太大影響,先上程式碼
private static String getMac() { try { Enumeration<NetworkInterface> el = NetworkInterface .getNetworkInterfaces(); while (el.hasMoreElements()) { byte[] mac = el.nextElement().getHardwareAddress(); if (mac == null) continue; String hexstr = bytesToHexString(mac); return getSplitString(hexstr,"-",2).toUpperCase(); } } catch (Exception exception) { exception.printStackTrace(); } return null; } public static String getMachineCode() throws Exception{ Set<String> result = new HashSet<>(); String mac = getMac(); result.add(mac); Properties props = System.getProperties(); String javaVersion = props.getProperty("java.version"); result.add(javaVersion); String javaVMVersion = props.getProperty("java.vm.version"); result.add(javaVMVersion); String osVersion = props.getProperty("os.version"); result.add(osVersion); String code = Encrpt.GetMD5Code(result.toString()); return getSplitString(code,4); }
這裡進行的操作是取出機器碼,與java版本,jvm,作業系統引數進行混合,並進行MD5操作
2.進行lic檔案的生成
這是我生成證書與進行授權證書的介面,可以看到授權證書主要包含三個要素,機器碼,是否永久有效標識,證書時效,我們會將這些資料寫入文字中並進行加密處理,看下生成證書的程式碼
public static void getLicense(String isNoTimeLimit,String licenseLimit,String machineCode,String licensePath,String priavateKeyPath) throws Exception{ String[] liccontent = { "[email protected]","LICENSENAME=YBLOG使用證書",MessageFormat.format("LICENSETYPE={0}",isNoTimeLimit),MessageFormat.format("EXPIREDAY={0}",licenseLimit),//日期採用yyyy-MM-dd日期格式 MessageFormat.format("MACHINECODE={0}",machineCode),"" }; //將lic內容進行混合簽名並寫入內容 StringBuilder sign = new StringBuilder(); for(String item:liccontent){ sign.append(item+"yblog"); } liccontent[5] = MessageFormat.format("LICENSESIGN={0}",Encrpt.GetMD5Code(sign.toString())); FileUtil.createFileAndWriteLines(licensePath,liccontent); //將寫入的內容整體加密替換 String filecontent =FileUtil.readFileToString(licensePath); String encrptfilecontent = Encrpt.EncriptWRSA_Pri(filecontent,priavateKeyPath); File file = new File(licensePath); file.delete(); FileUtil.createFile(licensePath,encrptfilecontent); }
這裡我們是將一些資訊與特定標識進行拼接然後加密,使用的是RSA加密,我們使用私鑰加密公鑰解密,保證驗證的開放性與生成證書的私密性,金鑰可以使用java自帶的keytool工具進行生成,
教程地址:http://note.youdao.com/noteshare?id=09e2bfc902b21a335a4505f7946a45c9
在lic檔案最後我們加上一個LICENSESIGN引數,對其他資訊進行一次加密,防止資訊被篡改,生成檔案後再對文字進行整體加密
這裡生成金鑰的長度為2048而非1024,所以解密塊長度為256,這裡需要注意下,公鑰加密方法為,為了方便大家,這裡提供下具體加密程式碼
private static final int MAX_ENCRYPT_BLOCK = 117; private static final int MAX_DECRYPT_BLOCK=256; public static String EncriptWRSA_Pri(String data,String path) throws Exception{ String encryptData =""; FileInputStream in = new FileInputStream(path); KeyStore ks = KeyStore.getInstance("JKS");// JKS: Java KeyStoreJKS,可以有多種型別 ks.load(in,"123".toCharArray()); in.close(); String alias = "yblogkey"; // 記錄的別名 String pswd = "123"; // 記錄的訪問密碼 java.security.cert.Certificate cert = ks.getCertificate(alias); //獲取私鑰 PrivateKey privateKey = (PrivateKey) ks.getKey(alias,pswd.toCharArray()); //私鑰加密 Cipher cipher = Cipher.getInstance("rsa"); SecureRandom random = new SecureRandom(); cipher.init(Cipher.ENCRYPT_MODE,privateKey,random); try { // Cipher cipher = Cipher.getInstance("RSA"); // cipher.init(Cipher.ENCRYPT_MODE,publicKey); int length = data.getBytes().length; int offset = 0; byte[] cache; ByteArrayOutputStream outStream = new ByteArrayOutputStream(); int i = 0; while(length - offset > 0){ if(length - offset > MAX_ENCRYPT_BLOCK){ cache = cipher.doFinal(data.getBytes(),offset,MAX_ENCRYPT_BLOCK); }else{ cache = cipher.doFinal(data.getBytes(),length - offset); } outStream.write(cache,cache.length); i++; offset = i * MAX_ENCRYPT_BLOCK; } return encode.encode(outStream.toByteArray()); } catch (IllegalBlockSizeException e) { e.printStackTrace(); } catch (BadPaddingException e) { e.printStackTrace(); } return encryptData; } public static String DecriptWithRSA_Pub(String data,String path) throws Exception{ X509Certificate x509Certificate = (X509Certificate) getCertificate(path); // 獲得公鑰 PublicKey publicKey = x509Certificate.getPublicKey(); Cipher cipher = Cipher.getInstance("rsa"); SecureRandom random = new SecureRandom(); byte[] bEncrypt = decoder.decodeBuffer(data); //公鑰解密 cipher.init(Cipher.DECRYPT_MODE,publicKey,random); String decryptData = ""; // byte[] plainData = cipher.doFinal(bEncrypt); // System.out.println("11111:"+new String(plainData)); int inputLen = bEncrypt.length; ByteArrayOutputStream out = new ByteArrayOutputStream(); int offSet = 0; byte[] cache; int i = 0; // 對資料分段解密 while (inputLen - offSet > 0) { if (inputLen - offSet > MAX_DECRYPT_BLOCK) { cache = cipher.doFinal(bEncrypt,offSet,MAX_DECRYPT_BLOCK); } else { cache = cipher.doFinal(bEncrypt,inputLen - offSet); } out.write(cache,cache.length); i++; offSet = i * MAX_DECRYPT_BLOCK; } byte[] decryptedData = out.toByteArray(); out.close(); return new String(decryptedData); }
3.驗證lic
我們會在系統中註冊一個攔截器,未通過系統授權認證會自動跳轉到lic檔案上傳介面,springboot接收檔案與常規java有一些不同,使用的MultipartFile物件,會獲取到上傳檔案的陣列,進行操作,看下儲存上傳lic檔案程式碼
@RequestMapping(value="/login/licenseauth",method= RequestMethod.POST) @ResponseBody public Map<Object,Object> licenseauth(MultipartHttpServletRequest multiReq){ Map<Object,Object> map = new HashMap<Object,Object>(); try { String savePath = ResourceUtils.getURL("src/main/resources/static/lic").getPath(); MultipartFile file = multiReq.getFile("file"); String filename = file.getOriginalFilename(); File uploadfile = new File(savePath + "\\" + filename); if (!uploadfile.exists()){ //獲取item中的上傳檔案的輸入流 InputStream in = file.getInputStream(); //建立一個檔案輸出流 FileOutputStream out = new FileOutputStream(savePath + "\\" + filename); //建立一個緩衝區 byte buffer[] = new byte[1024]; //判斷輸入流中的資料是否已經讀完的標識 int len = 0; //迴圈將輸入流讀入到緩衝區當中,(len=in.read(buffer))>0就表示in裡面還有資料 while((len=in.read(buffer))>0){ //使用FileOutputStream輸出流將緩衝區的資料寫入到指定的目錄(savePath + "\\" + filename)當中 out.write(buffer,len); } //關閉輸入流 in.close(); //關閉輸出流 out.close(); } map.put("executestatus","1"); }catch (Exception e){ e.printStackTrace(); map.put("executestatus","0"); } return map; }
有了上傳檔案,我們就可以通過系統內建的公鑰對lic檔案的機器碼,授權時間進行驗證,確定是否能正常訪問系統
public static boolean authLicense() throws Exception{ boolean isauth = false; String pubkpath = ResourceUtils.getURL("src/main/resources/static/lic/").getPath()+"yblog.crt"; String licpath = ResourceUtils.getURL("src/main/resources/static/lic/").getPath(); File lic = new File(licpath); String[] filelist = lic.list(); if (filelist.length>0){ for (int i = 0; i < filelist.length; i++) { if (filelist[i].contains(".lic")){ File readfile = new File(licpath + filelist[i]); if (readfile.isFile()) { String liccontent = FileUtil.readFileToString(readfile); String decriptliccontent = Encrpt.DecriptWithRSA_Pub(liccontent,pubkpath); HashMap<String,String> props = genDataFromArrayByte(decriptliccontent.getBytes()); String licenseid = props.get("LICENSEID"); String licensename= props.get("LICENSENAME"); String licensetype = props.get("LICENSETYPE"); String liclimit = props.get("EXPIREDAY"); String machinecode = props.get("MACHINECODE"); String lincensesign = props.get("LICENSESIGN"); //驗證簽名 String allinfogroup = "LICENSEID="+licenseid+"yblog"+"LICENSENAME="+licensename+"yblog"+ "LICENSETYPE="+licensetype+"yblog"+"EXPIREDAY="+liclimit+"yblog"+"MACHINECODE="+machinecode+"yblogyblog"; if (lincensesign.equals(Encrpt.GetMD5Code(allinfogroup))){ //驗證機器碼 if (getMachineCode().equals(machinecode)){ SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd"); Date bt=new Date(); Date et=sdf.parse(liclimit); //驗證時間 if(bt.compareTo(et)<=0){ isauth = true; System.out.println("註冊檔案:"+filelist[i]+",已通過驗證"); break; }else{ System.out.println("證書過期"); } }else{ System.out.println("機器碼不一致"); } }else{ System.out.println("簽名不一致"); } } } } }else{ System.out.println("未上傳證書"); } return isauth; }
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支援我們。