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 = { "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内容进行混合签名并写入内容 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工具进行生成,
教程地址:
在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(), 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; // 对数据分段解密 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.验证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, 0, 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; }
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
上一篇: 一群同事去KTV喝酒