您好,登錄后才能下訂單哦!
本篇內容主要講解“怎么使用自定義類加載器防止代碼被反編譯破解”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“怎么使用自定義類加載器防止代碼被反編譯破解”吧!
@Slf4j public class EncryptUtils { private static String secretKey = "test123456lyb-geek"+System.currentTimeMillis(); private EncryptUtils(){} public static void encrypt(String classFileSrcPath,String classFileDestPath) { System.out.println(secretKey); FileInputStream fis = null; FileOutputStream fos = null; try { fis = new FileInputStream(classFileSrcPath); fos = new FileOutputStream(classFileDestPath); int len; String[] arrs = secretKey.split("lyb-geek"); long key = Long.valueOf(arrs[1]); System.out.println("key:"+key); while((len = fis.read())!=-1){ byte data = (byte)(len + key + secretKey.length()); fos.write(data); } } catch (Exception e) { log.error("encrypt fail:"+e.getMessage(),e); }finally { if(fis != null){ try { fis.close(); } catch (IOException e) { e.printStackTrace(); } } if(fos != null){ try { fos.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
public static void main(String[] args) { String classFileSrcPath = classFileSrcPath("UserServiceImpl"); System.out.println("classFileSrcPath:--->"+classFileSrcPath); String classFileDestDir = ServiceGenerate.class.getClassLoader().getResource("META-INF/services/").getPath(); System.out.println("classFileDestDir:--->"+classFileDestDir); String classFileDestPath = classFileDestDir + "com.github.lybgeek.user.service.impl.UserServiceImpl.lyb"; EncryptUtils.encrypt(classFileSrcPath,classFileDestPath); }
打開反編譯工具jd-gui,把加密的代碼拖入jd-gui
打不開,至少說明不能用jd-gui來反編譯加密過的代碼。
我們打開正常的編譯的class文件,其內容形如下 從內容我們大概還是能看出一些東西,比如包名啥的。而打開加密后的文件,其內容如下 內容宛若天書
答案:既然有加密,自然可以通過解密來使用。那這個解密得存放在什么地方進行解密?
如果對類加載有一定了解的朋友,就會知道java的class文件是通過類加載器把class加載入jvm內存中,因此我們可以考慮把解密放在類加載器中。常用的類加載有啟動類加載器、擴展類加載器、系統類加載。我們正常classpath路徑下的類都是通過系統類加載器進行加載。而不巧這三個jdk提供的加載器沒法滿足我們的需求。因此我們只能自己實現我們的類加載器。其自定義加載器代碼如下
@Slf4j public class CustomClassLoader extends ClassLoader{ /** * 授權碼 */ private String secretKey; private String SECRETKEY_PREFIX = "lyb-geek"; /** * class文件的根目錄 */ private String classRootDir = "META-INF/services/"; public CustomClassLoader(String secretKey) { this.secretKey = secretKey; } public String getClassRootDir() { return classRootDir; } public void setClassRootDir(String classRootDir) { this.classRootDir = classRootDir; } @Override protected Class<?> findClass(String name) throws ClassNotFoundException { Class<?> clz = findLoadedClass(name); //先查詢有沒有加載過這個類。如果已經加載,則直接返回加載好的類。如果沒有,則加載新的類。 if(clz != null){ return clz; }else{ ClassLoader parent = this.getParent(); clz = getaClass(name, clz, parent); if(clz != null){ return clz; }else{ clz = getaClass(name); } } return clz; } private Class<?> getaClass(String name) throws ClassNotFoundException { Class<?> clz; byte[] classData = getClassData(name); if(classData == null){ throw new ClassNotFoundException(); }else{ clz = defineClass(name, classData, 0,classData.length); } return clz; } private Class<?> getaClass(String name, Class<?> clz, ClassLoader parent) { try { //委派給父類加載 clz = parent.loadClass(name); } catch (Exception e) { //log.warn("parent load class fail:"+ e.getMessage(),e); } return clz; } private byte[] getClassData(String classname){ if(StringUtils.isEmpty(secretKey) || !secretKey.contains(SECRETKEY_PREFIX) || secretKey.split(SECRETKEY_PREFIX).length != 2){ throw new RuntimeException("secretKey is illegal"); } String path = CustomClassLoader.class.getClassLoader().getResource("META-INF/services/").getPath() +"/"+ classname+".lyb"; InputStream is = null; ByteArrayOutputStream bas = null; try{ is = new FileInputStream(path); bas = new ByteArrayOutputStream(); int len; //解密 String[] arrs = secretKey.split(SECRETKEY_PREFIX); long key = Long.valueOf(arrs[1]); // System.out.println("key:"+key); while((len = is.read())!=-1){ byte data = (byte)(len - key - secretKey.length()); bas.write(data); } return bas.toByteArray(); }catch(Exception e){ e.printStackTrace(); return null; }finally{ try { if(is!=null){ is.close(); } } catch (IOException e) { log.error("encrypt fail:"+e.getMessage(),e); } try { if(bas!=null){ bas.close(); } } catch (IOException e) { e.printStackTrace(); } } } }
通過如下方式進行調用
public static void main(String[] args) throws Exception{ CustomClassLoader customClassLoader = new CustomClassLoader("test123456lyb-geek1603895713759"); Class clz = customClassLoader.loadClass("com.github.lybgeek.user.service.impl.UserServiceImpl"); if(clz != null){ Method method = clz.getMethod("list", User.class); method.invoke(clz.newInstance(),new User()); } }
答案: 通過spring提供的擴展點進行ioc容器注入
1、編寫bean定義,并注冊注冊bean定義
@Component public class ServiceBeanFactoryPostProcessor implements BeanFactoryPostProcessor { @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { Object secretKey = YmlUtils.getValue("lyb-geek.secretKey"); if(ObjectUtils.isEmpty(secretKey)){ throw new RuntimeException("secretKey can not be null,you maybe need to config in application.yml with key lyb-geek.secretKey"); } registerBean(beanFactory,secretKey.toString()); // setClassLoader(beanFactory,secretKey.toString()); } /** * 如果項目中引入了>spring-boot-devtools,則默認加載器為org.springframework.boot.devtools.restart.classloader.RestartClassLoader * 此時如果使用自定加載器,則需把bean的類加載器變更為AppClassLoader * @param beanFactory */ private void setClassLoader(ConfigurableListableBeanFactory beanFactory,String secretKey) { CustomClassLoader customClassLoader = new CustomClassLoader(secretKey); beanFactory.setBeanClassLoader(customClassLoader.getParent()); } private void registerBean(ConfigurableListableBeanFactory beanFactory,String secretKey){ DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) beanFactory; BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(UserService.class); GenericBeanDefinition definition = (GenericBeanDefinition) builder.getRawBeanDefinition(); definition.getPropertyValues().add("serviceClz",UserService.class); definition.getPropertyValues().add("serviceImplClzName", "com.github.lybgeek.user.service.impl.UserServiceImpl"); definition.getPropertyValues().add("secretKey", secretKey); definition.setBeanClass(ServiceFactoryBean.class); definition.setAutowireMode(GenericBeanDefinition.AUTOWIRE_BY_TYPE); String beanId = StringUtils.uncapitalize(UserService.class.getSimpleName()); defaultListableBeanFactory.registerBeanDefinition(beanId, definition); } }
2、如果是接口注入,還需通過FactoryBean進行貍貓換太子
@Data @AllArgsConstructor @NoArgsConstructor public class ServiceFactoryBean <T> implements FactoryBean<T> , ApplicationContextAware, InitializingBean { private ApplicationContext applicationContext; private Class<T> serviceClz; private String serviceImplClzName; private String secretKey; private Object targetObj; @Override public T getObject() throws Exception { return (T) targetObj; } @Override public Class<?> getObjectType() { return serviceClz; } @Override public void afterPropertiesSet() throws Exception { targetObj = ServiceFactory.create(secretKey,serviceImplClzName,applicationContext); } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } }
3、驗證是否整合成功
驗證示例代碼
@RestController @RequestMapping("/user") public class UserController { @Autowired private UserService userService; @PostMapping(value = "/save") public User save(User user){ User newUser = userService.save(user); return newUser; } }
能夠正常輸出,說明整合成功
到此,相信大家對“怎么使用自定義類加載器防止代碼被反編譯破解”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。