业务场景
在软件产品或者源码发布后,防止产品或者源码的外流导致核心竞争力被破解,直接影响商业价值,如何对产品进行加密,对产品的保护非常的重要。
1、办公软件-下载安装后如果没有进行授权,可以无限制的复制,导致产品被滥用,资金流失。
2、源码被窃取,通过源码解读,去除加密授权的拦截或者过滤功能,导致源码流失,资金损失,核心产品外泄。
如果去管控呢?
方法一、只对外发布产品安装包或者产品运行包,不提供核心源码,如果非要提供源码,也要控制核心工具类、验证类是jar包的形式使用,而非全部代码。
方法二、增加额外的验证机制,当发现拦截器或过滤器没有相应时候,采取代码破坏机制,动态删除一些核心的配置文件,并且将破解人的远程IP,机器码,授权源码信息发送给管理端。
方法三、代码混淆,源代码保护,代码逻辑混淆,代码加密,提高代码阅读成本
所有的加密、混淆、机制管控都是操作产品或者源码被泄露的手段,但是任何的手段都可以被破解,所以在实际过程中,除了增加手段之外还要结合其他的方式进行关联,比如签订合同、软著申请等等。
产品和源码认证授权加密
RSA非对称加密
什么是rsa非对称加密
一般场景 服务端和移动端 数据传输加密,用AES对称加密,同一个秘钥,当一端被获取后,数据都将被获取。
对称加密算法在加密和解密时使用的是同一个秘钥;而非对称加密算法需要两个密钥来进行加密和解密,这两个秘钥是公开密钥(public key,简称公钥)和私有密钥(private key,简称私钥)。
服务端-公钥加密 移动端-私钥解密。
与对称加密算法不同,非对称加密算法需要两个密钥:公开密钥(publickey)和私有密钥(privatekey)。公开密钥与私有密钥是一对,如果用公开密钥对数据进行加密,只有用对应的私有密钥才能解密;如果用私有密钥对数据进行加密,那么只有用对应的公开密钥才能解密。因为加密和解密使用的是两个不同的密钥,所以这种算法叫做非对称加密算法。
非对称加密的优缺点
非对称加密与对称加密相比,其安全性更好:对称加密的通信双方使用相同的秘钥,如果一方的秘钥遭泄露,那么整个通信就会被破解。而非对称加密使用一对秘钥,一个用来加密,一个用来解密,而且公钥是公开的,秘钥是自己保存的,不需要像对称加密那样在通信之前要先同步秘钥。
非对称加密的缺点是加密和解密花费时间长、速度慢,只适合对少量数据进行加密。
RSA加解密工具类
package com.qingfeng.util;import java.io.ByteArrayOutputStream;import java.security.Key;import java.security.KeyFactory;import java.security.KeyPair;import java.security.KeyPairGenerator;import java.security.PrivateKey;import java.security.PublicKey;import java.security.Signature;import java.security.interfaces.RSAPrivateKey;import java.security.interfaces.RSAPublicKey;import java.security.spec.PKCS8EncodedKeySpec;import java.security.spec.X509EncodedKeySpec;import java.util.HashMap;import java.util.Map;import javax.crypto.Cipher;import org.apache.commons.codec.binary.Base64;/*** <p>* rsa 加密工具*/public class RSAUtils {/*** 加密算法RSA*/public static final String KEY_ALGORITHM = "RSA";/*** 签名算法*/public static final String SIGNATURE_ALGORITHM = "MD5withRSA";/*** 获取公钥的key*/private static final String PUBLIC_KEY = "RSAPublicKey";/*** 获取私钥的key*/private static final String PRIVATE_KEY = "RSAPrivateKey";/*** RSA最大加密明文大小*/private static final int MAX_ENCRYPT_BLOCK = 117;/*** RSA最大解密密文大小*/private static final int MAX_DECRYPT_BLOCK = 128;/*** <p>* 生成密钥对(公钥和私钥)* </p>** @return* @throws Exception*/public static Map<String, Object> genKeyPair() throws Exception {KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM);keyPairGen.initialize(1024);KeyPair keyPair = keyPairGen.generateKeyPair();RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();Map<String, Object> keyMap = new HashMap<String, Object>(2);keyMap.put(PUBLIC_KEY, publicKey);keyMap.put(PRIVATE_KEY, privateKey);return keyMap;}/*** <p>* 用私钥对信息生成数字签名* </p>** @param data 已加密数据* @param privateKey 私钥(BASE64编码)** @return* @throws Exception*/public static String sign(byte[] data, String privateKey) throws Exception {byte[] keyBytes = decode(privateKey);PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);PrivateKey privateK = keyFactory.generatePrivate(pkcs8KeySpec);Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);signature.initSign(privateK);signature.update(data);return encode(signature.sign());}/*** <p>* 校验数字签名* </p>** @param data 已加密数据* @param publicKey 公钥(BASE64编码)* @param sign 数字签名** @return* @throws Exception**/public static boolean verify(byte[] data, String publicKey, String sign)throws Exception {byte[] keyBytes = decode(publicKey);X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);PublicKey publicK = keyFactory.generatePublic(keySpec);Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);signature.initVerify(publicK);signature.update(data);return signature.verify(decode(sign));}/*** <P>* 私钥解密* </p>** @param encryptedData 已加密数据* @param privateKey 私钥(BASE64编码)* @return* @throws Exception*/public static byte[] decryptByPrivateKey(byte[] encryptedData, String privateKey)throws Exception {byte[] keyBytes = decode(privateKey);PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);Key privateK = keyFactory.generatePrivate(pkcs8KeySpec);Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());cipher.init(Cipher.DECRYPT_MODE, privateK);int inputLen = encryptedData.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(encryptedData, offSet, MAX_DECRYPT_BLOCK);} else {cache = cipher.doFinal(encryptedData, offSet, inputLen - offSet);}out.write(cache, 0, cache.length);i++;offSet = i * MAX_DECRYPT_BLOCK;}byte[] decryptedData = out.toByteArray();out.close();return decryptedData;}/*** <p>* 公钥解密* </p>** @param encryptedData 已加密数据* @param publicKey 公钥(BASE64编码)* @return* @throws Exception*/public static byte[] decryptByPublicKey(byte[] encryptedData, String publicKey)throws Exception {byte[] keyBytes = decode(publicKey);X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);Key publicK = keyFactory.generatePublic(x509KeySpec);Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());cipher.init(Cipher.DECRYPT_MODE, publicK);int inputLen = encryptedData.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(encryptedData, offSet, MAX_DECRYPT_BLOCK);} else {cache = cipher.doFinal(encryptedData, offSet, inputLen - offSet);}out.write(cache, 0, cache.length);i++;offSet = i * MAX_DECRYPT_BLOCK;}byte[] decryptedData = out.toByteArray();out.close();return decryptedData;}/*** <p>* 公钥加密* </p>** @param data 源数据* @param publicKey 公钥(BASE64编码)* @return* @throws Exception*/public static byte[] encryptByPublicKey(byte[] data, String publicKey)throws Exception {byte[] keyBytes = decode(publicKey);X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);Key publicK = keyFactory.generatePublic(x509KeySpec);// 对数据加密Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());cipher.init(Cipher.ENCRYPT_MODE, publicK);int inputLen = data.length;ByteArrayOutputStream out = new ByteArrayOutputStream();int offSet = 0;byte[] cache;int i = 0;// 对数据分段加密while (inputLen - offSet > 0) {if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {cache = cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK);} else {cache = cipher.doFinal(data, offSet, inputLen - offSet);}out.write(cache, 0, cache.length);i++;offSet = i * MAX_ENCRYPT_BLOCK;}byte[] encryptedData = out.toByteArray();out.close();return encryptedData;}/*** <p>* 私钥加密* </p>** @param data 源数据* @param privateKey 私钥(BASE64编码)* @return* @throws Exception*/public static byte[] encryptByPrivateKey(byte[] data, String privateKey)throws Exception {byte[] keyBytes =decode(privateKey);PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);Key privateK = keyFactory.generatePrivate(pkcs8KeySpec);Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());cipher.init(Cipher.ENCRYPT_MODE, privateK);int inputLen = data.length;ByteArrayOutputStream out = new ByteArrayOutputStream();int offSet = 0;byte[] cache;int i = 0;// 对数据分段加密while (inputLen - offSet > 0) {if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {cache = cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK);} else {cache = cipher.doFinal(data, offSet, inputLen - offSet);}out.write(cache, 0, cache.length);i++;offSet = i * MAX_ENCRYPT_BLOCK;}byte[] encryptedData = out.toByteArray();out.close();return encryptedData;}/*** <p>* 获取私钥* </p>** @param keyMap 密钥对* @return* @throws Exception*/public static String getPrivateKey(Map<String, Object> keyMap)throws Exception {Key key = (Key) keyMap.get(PRIVATE_KEY);return encode(key.getEncoded());}/*** <p>* 获取公钥* </p>** @param keyMap 密钥对* @return* @throws Exception*/public static String getPublicKey(Map<String, Object> keyMap)throws Exception {Key key = (Key) keyMap.get(PUBLIC_KEY);return encode(key.getEncoded());}/*** base64 加密* @param bytes* @return* @throws Exception*/public static String encode(byte[] bytes) throws Exception {return new String(Base64.encodeBase64(bytes));}/*** base64 解密* @param base64* @return* @throws Exception*/public static byte[] decode(String base64) throws Exception {return Base64.decodeBase64(base64.getBytes());}/*** @Description: 解密* @Param: [data, privateKey]* @return: java.lang.String* @Author: anxingtao* @Date: 2019-3-13 11:06*/public static String decrypt(String data, String privateKey) throws Exception {byte[] pubdata = decryptByPrivateKey(decode(data), privateKey);return new String(pubdata, "UTF-8");}/*** @Description: 加密* @Param: [data, publicKey]* @return: java.lang.String* @Author: anxingtao* @Date: 2019-3-13 11:10*/public static String encrypt(String data, String publicKey) throws Exception {byte[] pubdata = encryptByPublicKey(data.getBytes("UTF-8"), publicKey);String outString = new String(encode(pubdata));return outString;}/*** @title encrypriva* @description 私钥加密* @author Administrator* @updateTime 2021/8/11 16:31*/public static String encrypk(String data, String privateKey) throws Exception {byte[] resdata = encryptByPrivateKey(data.getBytes("UTF-8"), privateKey);String outString = new String(encode(resdata));return outString;}/*** @title decrypk* @description 公钥解密* @author Administrator* @updateTime 2021/8/11 16:32*/public static String decrypk(String data, String publicKey) throws Exception {byte[] resdata = decryptByPublicKey(decode(data), publicKey);return new String(resdata, "UTF-8");}public static void main(String[] args) throws Exception {// String privateKey = "MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBALbGpPkikVf51rzHHr5rCmtIjpsq9Pked0W5xu5Khf4fQEl7utYahfC3dleFaCyLkD39Q1PpyOrQm7YQPSxkHR7Bn/w/CA93EYNKXG1cAZjDr/ZJAd6xUY2K4GkriWB9v4OeCTThkKwfharJJW270No5VT49ktKAHKkug/WnCUN7AgMBAAECgYEAlxPNK20qTFjj6biBLg5WV2VrEtFIGl7XYdf0meUZqnr0bYkLX4we6GENPby05hUaTlL4gvT8MTPrcWss1XOPKQYRrSjLBZmmCiYe+SDxx0ZPmNN1db86X0ahQsNzxAVkTyIKKVW1SxwyvIOSN5H71yyBzZj/yG0qymj8H0hBVFkCQQDijlLZ9E7pGT/2HLitxEyi02kIjlsgLtjmk8NP8if5eCspmKXaw3xH2j3BLOGDGZUjeQGZcyOOHdGnNkbUVSdtAkEAzoe6ugzr63r70CwbWAEBQLRUfX4i8fiLazlCybhVAMEKjbKTdcAUFLfKNmharPHE/dlHLja/dReaLMKB7l69hwJABcM/AkI/m5hD0zvJysm6dU3RVyFf2gK3C65ogmkTcToIRweV+GmOiLlZZseAePg2ne9fBgsytVO22Hz98jq0RQJAGOyOX0eR7RAhdYTtI9izMwDQNXjUdMke4ii946QoNfgV8vW7D/nHMpzffWNolfhzYoMnMO+QeWwIwiATGBY83wJBAN5aLGltsuW/6ILvMTgciwdQlO3dTjzFXhbXURsyKWYF6QJnaeqNMVqr9F0lWXKQyuIu+mj5JRkZszOvS0jczNA=\n";// String publicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC2xqT5IpFX+da8xx6+awprSI6bKvT5HndFucbuSoX+H0BJe7rWGoXwt3ZXhWgsi5A9/UNT6cjq0Ju2ED0sZB0ewZ/8PwgPdxGDSlxtXAGYw6/2SQHesVGNiuBpK4lgfb+Dngk04ZCsH4WqySVtu9DaOVU+PZLSgBypLoP1pwlDewIDAQAB";// String msg = "{\"dev_id\":\"868989034328943\",\"user_id\":\"\",\"team_id\":\"\"}";// String msg = DateTimeUtil.getDate();// String sm = RSAUtils.encrypt(msg,publicKey);// System.out.println(sm);// String str = "IGCJsmAEdYDle/WYAS4qd3hCp9TAZRz806Sm8p4KepJ2XuDaOnKh65homSgZJ88/pQs5rEm6Qmsg+8JqMgqjixD8jZhnToVriKzkhHe2UarS+sT8oUAvXrO98FwZv7GELgt4mCuitwxrREYq4QUtwI6Fc+SGdF0BjeKNUSWHifg=";//////// String s = RSAUtils.decrypt(sm,privateKey);// System.out.println(s);//生成密钥对(公钥和私钥)Map map = RSAUtils.genKeyPair();System.out.println("----------------公钥----------------");System.out.println(RSAUtils.getPublicKey(map));System.out.println("----------------私钥----------------");System.out.println(RSAUtils.getPrivateKey(map));// String str = RSAUtils.encrypk("我爱你中国",privateKey);// System.out.println(str);//// String res = RSAUtils.decrypk(str,publicKey);// System.out.println(res);// String content = "F2cgqkTN1DehdikNnKtt5t0p+HrOr4hwWrbf9MGmksWUQFwWzFPbwt1at7uXYp8mt88KiIHrD/yKe8pkdzuq2VMTyvObryIbVannBHtzrhjBd2FoRQpaorMWtFuv7MZY3WmuqhzdhXMXAx1pB/hmEToncvW3ZpEWp7N5ItO89H8=";// String priKey = "MIICYQIBAAKBgQCijkoTGTel/S62WwUpXswc8m7Fdp+SY2ZMZKV8FeRD5QUi1mGYUHqmab7w/xN2vdNWI9sR9MPL4aO2IA57HD4HTGmidyCDmxl8QCRMSNwXroIP6mWpI5lHjajPth25n2gwsnYoAvrSfTnTkUAmoKUtQSg3/AZ9Rg8mf8NFY5AjGwIDAQABAoGBAJqS8W9NyHPn2DaBQNxBD5jrE1hj34NFT+6OuinPa1sAeSzSbMV4qdh6r53dADYmdcLwn41okZLbAmDaBBscUUZo/pIUPdGgU+P7RqpMtfwbq/RoleFpU2GWBErJ7/qKe6cfcEyuBhAMG8vLqtfA70P75vFAMoKhzWsUGtD5gQVZAkUAuRjD839QS/u17Xv6jWbcw0vxTD2UN/hyObyQORlX+LjxC5RCuYwkkWoXIGe94hzsyCoURc6RhakRM/LyqhCliTQ+PvcCPQDg0xUHm2qr8jfo1PTYf6Ks8Id9FZiqxD1VJh2OoRMSTCPo8O4PskbzictNu2siFLt0r3EKjCDP1jP0H/0CRFSsO5d8OiNINmU5PdjJoVvFtdCGqvMfuEEpPWChc1jYYYxGem+e6GuM+J9eVcLGMJswhK2aXX+jY7c8AD5D9zXYrFDpAj0Ap5OOXEIyy4FavRhmfCz+wyrxwoFjbv2gvaQQaeyTu5K3PXy/5UE783Ek8Yad/yQ26W2Ps43pMyF1TiS9AkQrfddTmrz7OZEfFtgaHuSj/8gbkGEdr7H8YIML/wZ1ozDkfxwsnzgclJXir/Qb2VjGOSzfUrlj2SaxgqIGfKI7DyEWeg==";// System.out.println(priKey);//// String priKey2 = "MIICewIBADANBgkqhkiG9w0BAQEFAASCAmUwggJhAgEAAoGBAKKOShMZN6X9LrZbBSlezBzybsV2n5JjZkxkpXwV5EPlBSLWYZhQeqZpvvD/E3a901Yj2xH0w8vho7YgDnscPgdMaaJ3IIObGXxAJExI3Beugg/qZakjmUeNqM+2HbmfaDCydigC+tJ9OdORQCagpS1BKDf8Bn1GDyZ/w0VjkCMbAgMBAAECgYEAmpLxb03Ic+fYNoFA3EEPmOsTWGPfg0VP7o66Kc9rWwB5LNJsxXip2Hqvnd0ANiZ1wvCfjWiRktsCYNoEGxxRRmj+khQ90aBT4/tGqky1/Bur9GiV4WlTYZYESsnv+op7px9wTK4GEAwby8uq18DvQ/vm8UAygqHNaxQa0PmBBVkCRQC5GMPzf1BL+7Xte/qNZtzDS/FMPZQ3+HI5vJA5GVf4uPELlEK5jCSRahcgZ73iHOzIKhRFzpGFqREz8vKqEKWJND4+9wI9AODTFQebaqvyN+jU9Nh/oqzwh30VmKrEPVUmHY6hExJMI+jw7g+yRvOJy027ayIUu3SvcQqMIM/WM/Qf/QJEVKw7l3w6I0g2ZTk92MmhW8W10Iaq8x+4QSk9YKFzWNhhjEZ6b57oa4z4n15VwsYwmzCErZpdf6NjtzwAPkP3NdisUOkCPQCnk45cQjLLgVq9GGZ8LP7DKvHCgWNu/aC9pBBp7JO7krc9fL/lQTvzcSTxhp3/JDbpbY+zjekzIXVOJL0CRCt911OavPs5kR8W2Boe5KP/yBuQYR2vsfxggwv/BnWjMOR/HCyfOByUleKv9BvZWMY5LN9SuWPZJrGCogZ8ojsPIRZ6";//// System.out.println(RSAUtils.decrypt(content,priKey2));}}
新增证书颁发模块
证书模块功能效果
核心代码解析
/*** @title exportCert* @description 导出证书* @author Administrator* @updateTime 2021/8/11 15:46*/@RequiresPermissions("authCert:exportCert")@RequestMapping(value = "/exportCert", method = RequestMethod.GET)public void exportCert(HttpServletRequest request, HttpServletResponse response, HttpSession session) throws Exception {PageData pd = new PageData(request);PageData p = authCertService.findInfo(pd);String content = p.get("title")+"#"+p.get("start_time")+"#"+p.get("end_time")+"#"+p.get("ip_address")+"#"+p.get("mac_address")+"#"+p.get("other_limit");System.out.println(content);System.out.println(commonConfig.getPublicKey());String license = RSAUtils.encrypt(content,commonConfig.getPublicKey());// 创建根节点Element root = new Element("QingFeng");// 为根节点设置属性root.setAttribute("version", p.get("version_num").toString());// 创建Document对象,并为其设置根节点Document document = new Document(root);Element title = new Element("Title");Element sdate = new Element("SDate");Element edate = new Element("EDate");Element ip = new Element("IP");Element mac = new Element("MAC");Element olimit = new Element("OLimit");Element licenseKeys = new Element("LicenseKeys");title.addContent(p.get("title").toString());sdate.addContent(p.get("start_time").toString());edate.addContent(p.get("end_time").toString());ip.addContent(p.get("ip_address")+"");mac.addContent(p.get("mac_address")+"");olimit.addContent(p.get("other_limit")+"");licenseKeys.addContent(license);root.addContent(title);root.addContent(sdate);root.addContent(edate);root.addContent(ip);root.addContent(mac);root.addContent(olimit);root.addContent(licenseKeys);// 创建XMLOutputter对象XMLOutputter outputter = new XMLOutputter();//创建Format对象(自动缩进、换行)Format format = Format.getPrettyFormat();//为XMLOutputter设置Format对象outputter.setFormat(format);// 将Document转换成XMLString tempPath = "D:\\home\\template\\QingfengLicense.xml";// String tempPath = session.getServletContext().getRealPath("/") + "/template/QingfengLicense.xml";outputter.output(document, new FileOutputStream(new File(tempPath)));// String toPath = session.getServletContext().getRealPath("/") + "/template/QingfengLicense.zip";/** 测试压缩方法2 */// List<File> fileList = new ArrayList<>();// fileList.add(new File(tempPath));// FileOutputStream fos2 = new FileOutputStream(new File(toPath));// ZipUtils.toZip(fileList, fos2);FileUtil.downFile(response, tempPath, "QingfengLicense.xml");// File file = new File(toPath);// file.delete();// file.deleteOnExit();}
证书下载
public static void downFile(HttpServletResponse response, String filePath,String filename) throws Exception {File tempFile = new File(filePath);System.out.println(tempFile.exists());if (tempFile.exists()) {response.reset();response.setContentType("bin");//filename = new String(filename.getBytes(), "ISO-8859-1");response.addHeader("Content-Disposition", "attachment;filename="+ filename);FileInputStream fis = new FileInputStream(tempFile);OutputStream os = response.getOutputStream();byte[] bb = new byte[1024*8];int i = 0;while ((i = fis.read(bb)) > 0) {os.write(bb, 0, i);}os.close();os.flush();fis.close();}}
证书认证校验-项目启动
证书校验源码
/*** 继承Application接口后项目启动时会按照执行顺序执行run方法* 通过设置Order的value来指定执行的顺序*/@Component@Order(value = 1)public class StartService implements ApplicationRunner {@Autowiredprivate CommonConfig commonConfig;@Overridepublic void run(ApplicationArguments applicationArguments) throws Exception {ClassPathResource classPathResource = new ClassPathResource("config/QingfengLicense.xml");// InputStream inputStream =classPathResource.getInputStream();// 创建SAXReader对象SAXReader reader = new SAXReader();// 加载xml文件Document dc= reader.read(classPathResource.getFile());// 获取根节点Element e = dc.getRootElement();System.out.println(e);// 获取迭代器Iterator it = e.elementIterator();// 遍历迭代器,获取根节点信息PageData pd = new PageData();while(it.hasNext()){Element book = (Element) it.next();pd.put(book.getName(),book.getStringValue());}String LicenseKeys = pd.get("LicenseKeys").toString();String license = RSAUtils.decrypt(LicenseKeys,commonConfig.getPrivateKey());if(Verify.verifyIsNull(license)){throw new RuntimeException("正式认证失败。");}else{System.out.println(license);String[] params = license.split("#", -1);System.out.println(params.length);String title = params[0];String start_time = params[1];String end_time = params[2];String ip_address = params[3];String mac_address = params[4];String other_limit = params[5];SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");Date startDate = sdf.parse(start_time);Date endDate = sdf.parse(end_time);if(startDate.after(DateTimeUtil.getDDate()) || endDate.before(DateTimeUtil.getDDate())){throw new RuntimeException("认证正式已失效,请重新授权。");}System.out.println(IpUtils.getIpAddress());System.out.println(IpUtils.getMacAddress());if(Verify.verifyIsNotNull(ip_address)){if(!ip_address.equals(IpUtils.getIpAddress())){throw new RuntimeException("授权IP无效,请重新授权。");}}if(Verify.verifyIsNotNull(mac_address)){if(!mac_address.equals(IpUtils.getMacAddress())){throw new RuntimeException("授权MAC无效,请重新授权。");}}}// System.out.println("###############################项目启动成功###############################");}}
ApplicationRunner接口功能说明
1、ApplicationRunner
是一个接口,常用于项目启动后,(也就是ApringApplication.run()执行结束),立马执行某些逻辑。
可用于项目的准备工作,比如加载配置文件,加载执行流,定时任务等等。
2、如何使用ApplicationRunner
可以有多个实例实现该接口,但是一般需要增加注解@Order来指定加载顺序
@Order(-1)优先于@Order(0)
@Order(1)优先于@Order(2)
@Component@Order(2)public class QingfengRunner implements ApplicationRunner {@Overridepublic void run(ApplicationArguments args) throws Exception {System.out.println(args);System.out.println("这个是测试ApplicationRunner接口");}}
3、还有个接口,也可以实现和ApplicationRunner一样的功能
CommandLineRunner
CommandLineRunner接口的run方法接收的参数为String数组
@Component@Order(value = 2)public class MyStartupRunner1 implements CommandLineRunner{@Overridepublic void run(String... strings) throws Exception {System.out.println(">>>>>>>>>>>>>>>服务启动执行,执行加载数据等操作 MyStartupRunner1 order 2 <<<<<<<<<<<<<");}}@Component@Order(value = 1)public class MyStartupRunner2 implements CommandLineRunner {@Overridepublic void run(String... strings) throws Exception {System.out.println(">>>>>>>>>>>>>>>服务启动执行,执行加载数据等操作 MyStartupRunner2 order 1 <<<<<<<<<<<<<");}}
证书认证校验-过滤器
过滤器证书校验源码
@Configurationpublic class WebFilterConfig {@Beanpublic FilterRegistrationBean filterDemo3Registration() {FilterRegistrationBean registration = new FilterRegistrationBean();registration.setFilter(WebUrlFilter());registration.addUrlPatterns("/*");registration.addInitParameter("paramName", "paramValue");registration.setName("filterDemo3");registration.setOrder(1);return registration;}@Beanpublic Filter WebUrlFilter() {return new WebUrlFilter();}}
public class WebUrlFilter implements Filter {private final Logger log = LoggerFactory.getLogger(getClass());@Autowiredprivate CommonConfig commonConfig;@Overridepublic void init(FilterConfig arg0) throws ServletException {System.out.println("doFilter 1");}@SneakyThrows@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {// request.setAttribute("menuAuthParams","add,edit,del");System.out.println("doFilter 2");ClassPathResource classPathResource = new ClassPathResource("config/QingfengLicense.xml");// InputStream inputStream =classPathResource.getInputStream();// 创建SAXReader对象SAXReader reader = new SAXReader();// 加载xml文件Document dc= reader.read(classPathResource.getFile());// 获取根节点Element e = dc.getRootElement();System.out.println(e);// 获取迭代器Iterator it = e.elementIterator();// 遍历迭代器,获取根节点信息PageData pd = new PageData();while(it.hasNext()){Element book = (Element) it.next();pd.put(book.getName(),book.getStringValue());}String LicenseKeys = pd.get("LicenseKeys").toString();String license = RSAUtils.decrypt(LicenseKeys,commonConfig.getPrivateKey());if(Verify.verifyIsNull(license)){throw new RuntimeException("正式认证失败。");}else{System.out.println(license);String[] params = license.split("#", -1);System.out.println(params.length);String title = params[0];String start_time = params[1];String end_time = params[2];String ip_address = params[3];String mac_address = params[4];String other_limit = params[5];SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");Date startDate = sdf.parse(start_time);Date endDate = sdf.parse(end_time);if(startDate.after(DateTimeUtil.getDDate()) || endDate.before(DateTimeUtil.getDDate())){throw new RuntimeException("认证正式已失效,请重新授权。");}System.out.println(IpUtils.getIpAddress());System.out.println(IpUtils.getMacAddress());if(Verify.verifyIsNotNull(ip_address)){if(!ip_address.equals(IpUtils.getIpAddress())){throw new RuntimeException("授权IP无效,请重新授权。");}}if(Verify.verifyIsNotNull(mac_address)){if(!mac_address.equals(IpUtils.getMacAddress())){throw new RuntimeException("授权MAC无效,请重新授权。");}}}filterChain.doFilter(request, response);}@Overridepublic void destroy() {System.out.println("doFilter 3");}}
什么是过滤器
过滤器的概念
在讲Spring boot之前,我们先了解一下过滤器和拦截器。这两者在功能方面很类似,但是在具体技术实现方面,差距还是比较大的。在分析两者的区别之前,我们先理解一下AOP的概念,AOP不是一种具体的技术,而是一种编程思想。在面向对象编程的过程中,我们很容易通过继承、多态来解决纵向扩展。 但是对于横向的功能,比如,在所有的service方法中开启事务,或者统一记录日志等功能,面向对象的是无法解决的。所以AOP——面向切面编程其实是面向对象编程思想的一个补充。而我们今天讲的过滤器和拦截器都属于面向切面编程的具体实现。而两者的主要区别包括以下几个方面:
1、Filter是依赖于Servlet容器,属于Servlet规范的一部分,而拦截器则是独立存在的,可以在任何情况下使用。
2、Filter的执行由Servlet容器回调完成,而拦截器通常通过动态代理的方式来执行。
3、Filter的生命周期由Servlet容器管理,而拦截器则可以通过IoC容器来管理,因此可以通过注入等方式来获取其他Bean的实例,因此使用会更方便。
过滤器的使用场景
Java过滤器是处于客户端与服务器资源文件之间的一道过滤网,在访问资源文件之前,通过一系列的过滤器可以对请求进行修改、判断等,把不符合规则的请求在中途拦截或修改;也可以对响应进行过滤,拦截或修改响应。
如图,浏览器发出的请求先递交给第一个filter进行过滤,符合规则则放行,递交给filter链中的下一个过滤器进行过滤。过滤器在链中的顺序与它在web.xml中配置的顺序有关,配置在前的则位于链的前端。当请求通过了链中所有过滤器后就可以访问资源文件了,如果不能通过,则可能在中间某个过滤器中被处理掉。
在doFilter()方法中,chain.doFilter()前的一般是对request执行的过滤操作,chain.doFilter后面的代码一般是对response执行的操作。过滤链代码的执行顺序如下:
过滤器一般用于登录权限验证、资源访问权限控制、敏感词汇过滤、字符编码转换等等操作,便于代码重用,不必每个servlet中还要进行相应的操作。
代码混淆ProGuard
ProGuard简介
ProGuard是一个混淆代码的开源项目,它的主要作用是混淆代码,ProGuard还包括以下4个功能。
- 压缩(Shrink):检测并移除代码中无用的类、字段、方法和特性(Attribute)。
- 优化(Optimize):对字节码进行优化,移除无用的指令。
- 混淆(Obfuscate):使用a,b,c,d这样简短而无意义的名称,对类、字段和方法进行重命名。
- 预检(Preveirfy):在Java平台上对处理后的代码进行预检,确保加载的class文件是可执行的。
Proguard是一个Java类文件压缩器、优化器、混淆器、预校验器。压缩环节会检测以及移除没有用到的类、字段、方法以及属性。优化环节会分析以及优化方法的字节码。混淆环节会用无意义的短变量去重命名类、变量、方法。这些步骤让代码更精简,更高效,也更难被逆向(破解)。
Springboot整合ProGuard代码混淆
<!-- proguard混淆插件--><plugin><groupId>com.github.wvengen</groupId><artifactId>proguard-maven-plugin</artifactId><version>2.2.0</version><executions><execution><!-- 打包的时候开始混淆--><phase>package</phase><goals><goal>proguard</goal></goals></execution></executions><configuration><injar>${project.build.finalName}.jar</injar><!--输出的jar--><outjar>${project.build.finalName}.jar</outjar><!-- 是否混淆--><obfuscate>true</obfuscate><options><option>-target 1.8</option> <!--指定java版本号--><option>-dontshrink</option> <!--默认开启,不做收缩(删除注释、未被引用代码)--><option>-dontoptimize</option><!--默认是开启的,这里关闭字节码级别的优化--><!-- 不路过非公用类文件及成员--><option>-dontskipnonpubliclibraryclasses</option><option>-dontskipnonpubliclibraryclassmembers</option><!--不用大小写混合类名机制--><option>-dontusemixedcaseclassnames</option><!-- 优化时允许访问并修改有修饰符的类和类的成员 --><option>-allowaccessmodification</option><!-- 确定统一的混淆类的成员名称来增加混淆--><option>-useuniqueclassmembernames</option><!-- 不混淆所有包名--><!--<option>-keeppackagenames</option>--><option>-adaptclassstrings</option><!--混淆类名之后,对使用Class.forName('className')之类的地方进行相应替代--><option>-ignorewarnings</option><!-- 忽略warn消息,如果提示org.apache.http.* 这个包里的类有问题,那么就加入下述代码:-keep class org.apache.http.** { *; } -dontwarn org.apache.http.**--><option>-keep class org.apache.logging.log4j.util.* { *; }</option><option>-dontwarn org.apache.logging.log4j.util.**</option><option>-keepattributesExceptions,InnerClasses,Signature,Deprecated,SourceFile,LineNumberTable,*Annotation*,EnclosingMethod</option><!--对异常、注解信息在runtime予以保留,不然影响springboot启动--><!--不混淆所有interface接口--><!-- <option>-keepnames interface **</option>--><option>-keepclassmembers enum * { *; }</option><!--保留枚举成员及方法--><option>-keepparameternames</option><option>-keepclasseswithmembers public class * {public static void main(java.lang.String[]);}</option> <!--保留main方法的类及其方法名--><!--忽略note消息,如果提示javax.annotation有问题,那麽就加入以下代码--><option>-dontnote javax.annotation.**</option><option>-dontnote sun.applet.**</option><option>-dontnote sun.tools.jar.**</option><option>-dontnote org.apache.commons.logging.**</option><option>-dontnote javax.inject.**</option><option>-dontnote org.aopalliance.intercept.**</option><option>-dontnote org.aopalliance.aop.**</option><option>-dontnote org.apache.logging.log4j.**</option><!-- ##### 以下为需要根据项目情况修改 comment by 青锋 ##### --><!--入口程序类不能混淆,混淆会导致springboot启动不了--><option>-keep class com.qingfeng.Application</option><option>-keep class com.qingfeng.framework.**.* {*;}</option><option>-keep class com.qingfeng.quartz.**.* {*;}</option><!-- <option>-keep class com.qingfeng.system.entity.* {*;}</option>--><!-- <option>-keep class com.qingfeng.system.model.* {*;}</option>--><!-- <option>-keep class com.qingfeng.system.controller.* {*;}</option>--><!-- ##### 以上为需要根据项目情况修改 comment by qingfeng ##### --><option>-keep interface * extends * { *; }</option><!--不混淆所有类,保存原始定义的注释--><option>-keepclassmembers class * {@org.springframework.beans.factory.annotation.Autowired *;@org.springframework.beans.factory.annotation.Value *;}</option></options><libs><!-- 添加依赖 java8--><lib>${java.home}/lib/rt.jar</lib><lib>${java.home}/lib/jce.jar</lib></libs></configuration><dependencies><!-- https://mvnrepository.com/artifact/net.sf.proguard/proguard-base --><dependency><groupId>net.sf.proguard</groupId><artifactId>proguard-base</artifactId><version>6.1.1</version></dependency></dependencies></plugin><!--Springboot repackage 打包--><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><executions><execution><!-- spingboot 打包需要repackage否则不是可执行jar --><goals><goal>repackage</goal></goals><configuration><mainClass>com.qingfeng.Application</mainClass></configuration></execution></executions></plugin>
问题:
1.由于在混淆后代码a.class,b.class,c.class等方式,会导致springboot注入bean的时候报bean冲突,需要对生成bean策略进行修改,添加类:
package com.qingfeng;import org.springframework.beans.factory.config.BeanDefinition;import org.springframework.beans.factory.support.BeanDefinitionRegistry;import org.springframework.context.annotation.AnnotationBeanNameGenerator;/*** @author Administrator* @version 1.0.0* @ProjectName qingfengThymeleaf* @Description sping下代码混淆后,不同包下的bean名相同会报bean冲突* @createTime 2022年04月25日 22:59:00*/public class UniqueNameGenerator extends AnnotationBeanNameGenerator {@Overridepublic String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {//全限定类名String beanName = definition.getBeanClassName();return beanName;}}
@ComponentScan(nameGenerator = UniqueNameGenerator.class)
执行maven打包
jd-gui查看编译后的class文件
打包之后,查看打包后的代码
项目运行




