Springboot如何實(shí)現(xiàn)Web系統(tǒng)License授權(quán)認(rèn)證
在我們做系統(tǒng)級框架的時(shí)候,我們要一定程度上考慮系統(tǒng)的使用版權(quán),不能隨便一個(gè)人拿去在任何環(huán)境都能用,所以我們需要給我們系統(tǒng)做一個(gè)授權(quán)認(rèn)證機(jī)制,只有上傳了我們下發(fā)的lic文件并驗(yàn)證通過,才能正常使用,下面就開始一步一步實(shí)現(xiàn)這個(gè)功能
1.生成機(jī)器碼
我們首先要做的就是對軟件部署的環(huán)境的唯一性進(jìn)行限制,這里使用的是macadderss,當(dāng)然你也可以換成cpu序列編號(hào),并無太大影響,先上代碼
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);
}
這里進(jìn)行的操作是取出機(jī)器碼,與java版本,jvm,操作系統(tǒng)參數(shù)進(jìn)行混合,并進(jìn)行MD5操作
2.進(jìn)行l(wèi)ic文件的生成


這是我生成證書與進(jìn)行授權(quán)證書的界面,可以看到授權(quán)證書主要包含三個(gè)要素,機(jī)器碼,是否永久有效標(biāo)識(shí),證書時(shí)效,我們會(huì)將這些數(shù)據(jù)寫入文本中并進(jìn)行加密處理,看下生成證書的代碼
public static void getLicense(String isNoTimeLimit, String licenseLimit, String machineCode, String licensePath, String priavateKeyPath) throws Exception{
String[] liccontent = {
"LICENSEID=yanpeng19940119@gmail.com",
"LICENSENAME=YBLOG使用證書",
MessageFormat.format("LICENSETYPE={0}",isNoTimeLimit),
MessageFormat.format("EXPIREDAY={0}",licenseLimit), //日期采用yyyy-MM-dd日期格式
MessageFormat.format("MACHINECODE={0}",machineCode),
""
};
//將lic內(nèi)容進(jìn)行混合簽名并寫入內(nèi)容
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);
//將寫入的內(nèi)容整體加密替換
String filecontent =FileUtil.readFileToString(licensePath);
String encrptfilecontent = Encrpt.EncriptWRSA_Pri(filecontent,priavateKeyPath);
File file = new File(licensePath);
file.delete();
FileUtil.createFile(licensePath,encrptfilecontent);
}
這里我們是將一些信息與特定標(biāo)識(shí)進(jìn)行拼接然后加密,使用的是RSA加密,我們使用私鑰加密公鑰解密,保證驗(yàn)證的開放性與生成證書的私密性,密鑰可以使用java自帶的keytool工具進(jìn)行生成,
教程地址:http://note.youdao.com/noteshare?id=09e2bfc902b21a335a4505f7946a45c9
在lic文件最后我們加上一個(gè)LICENSESIGN參數(shù),對其他信息進(jìn)行一次加密,防止信息被篡改,生成文件后再對文本進(jìn)行整體加密
這里生成密鑰的長度為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(), offset, length - offset);
}
outStream.write(cache, 0, 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;
// 對數(shù)據(jù)分段解密
while (inputLen - offSet > 0) {
if (inputLen - offSet > MAX_DECRYPT_BLOCK) {
cache = cipher.doFinal(bEncrypt, offSet, MAX_DECRYPT_BLOCK);
} else {
cache = cipher.doFinal(bEncrypt, offSet, inputLen - offSet);
}
out.write(cache, 0, cache.length);
i++;
offSet = i * MAX_DECRYPT_BLOCK;
}
byte[] decryptedData = out.toByteArray();
out.close();
return new String(decryptedData);
}
3.驗(yàn)證lic
我們會(huì)在系統(tǒng)中注冊一個(gè)攔截器,未通過系統(tǒng)授權(quán)認(rèn)證會(huì)自動(dòng)跳轉(zhuǎn)到lic文件上傳界面,springboot接收文件與常規(guī)java有一些不同,使用的MultipartFile對象,會(huì)獲取到上傳文件的數(shù)組,進(jìn)行操作,看下保存上傳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();
//創(chuàng)建一個(gè)文件輸出流
FileOutputStream out = new FileOutputStream(savePath + "\\" + filename);
//創(chuàng)建一個(gè)緩沖區(qū)
byte buffer[] = new byte[1024];
//判斷輸入流中的數(shù)據(jù)是否已經(jīng)讀完的標(biāo)識(shí)
int len = 0;
//循環(huán)將輸入流讀入到緩沖區(qū)當(dāng)中,(len=in.read(buffer))>0就表示in里面還有數(shù)據(jù)
while((len=in.read(buffer))>0){
//使用FileOutputStream輸出流將緩沖區(qū)的數(shù)據(jù)寫入到指定的目錄(savePath + "\\" + filename)當(dāng)中
out.write(buffer, 0, len);
}
//關(guān)閉輸入流
in.close();
//關(guān)閉輸出流
out.close();
}
map.put("executestatus","1");
}catch (Exception e){
e.printStackTrace();
map.put("executestatus","0");
}
return map;
}
有了上傳文件,我們就可以通過系統(tǒng)內(nèi)置的公鑰對lic文件的機(jī)器碼,授權(quán)時(shí)間進(jìn)行驗(yàn)證,確定是否能正常訪問系統(tǒng)
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");
//驗(yàn)證簽名
String allinfogroup = "LICENSEID="+licenseid+"yblog"+"LICENSENAME="+licensename+"yblog"+
"LICENSETYPE="+licensetype+"yblog"+"EXPIREDAY="+liclimit+"yblog"+"MACHINECODE="+machinecode+"yblogyblog";
if (lincensesign.equals(Encrpt.GetMD5Code(allinfogroup))){
//驗(yàn)證機(jī)器碼
if (getMachineCode().equals(machinecode)){
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd");
Date bt=new Date();
Date et=sdf.parse(liclimit);
//驗(yàn)證時(shí)間
if(bt.compareTo(et)<=0){
isauth = true;
System.out.println("注冊文件:"+filelist[i]+",已通過驗(yàn)證");
break;
}else{
System.out.println("證書過期");
}
}else{
System.out.println("機(jī)器碼不一致");
}
}else{
System.out.println("簽名不一致");
}
}
}
}
}else{
System.out.println("未上傳證書");
}
return isauth;
}
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
SpringBoot2.0整合Shiro框架實(shí)現(xiàn)用戶權(quán)限管理的示例
這篇文章主要介紹了SpringBoot2.0整合Shiro框架實(shí)現(xiàn)用戶權(quán)限管理的示例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-08-08
Java HashMap在遍歷時(shí)刪除元素的實(shí)現(xiàn)
本文主要介紹了Java HashMap在遍歷時(shí)刪除元素的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-12-12
Apache?SkyWalking?監(jiān)控?MySQL?Server?實(shí)戰(zhàn)解析
這篇文章主要介紹了Apache?SkyWalking?監(jiān)控?MySQL?Server?實(shí)戰(zhàn)解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-09-09
詳解Java 序列化與反序列化(Serialization)
這篇文章主要介紹了Java 序列化與反序列化(Serialization),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí) 吧2019-03-03
生產(chǎn)消費(fèi)者模式實(shí)現(xiàn)方式和線程安全問題代碼示例
這篇文章主要介紹了生產(chǎn)消費(fèi)者模式實(shí)現(xiàn)方式和線程安全問題代碼示例,具有一定借鑒價(jià)值,需要的朋友可以參考下2017-12-12

