Java Bukkit 从 InputStream 加载 .jar 文件

Java Bukkit load .jar file from InputStream

你好 Whosebug 社区,

我有一个关于 java 和 bukkit 的问题。我有一个加密的插件,我不想将解密的文件保存在磁盘上。所以我使用了 InputStream。但我现在的问题是如何将这个文件注入我的世界(bukkit)。 是否有可用的自定义类加载器?我搜索了很多,但没有找到任何可行的解决方案。

我用 AES-128 解密了一个加密的插件:

    FileInputStream fin; 
    CipherInputStream cin;
    int nread = 0;
    byte [] inbuf = new byte [MAX_FILE_BUF];

    fin = new FileInputStream (input);
    cin = new CipherInputStream (fin, mDecipher);
    ByteArrayOutputStream baos = new ByteArrayOutputStream();

    while ((nread = cin.read (inbuf)) > 0 )
    {
        byte[] trimbuf = new byte [nread];
        for (int i = 0; i < nread; i++)
        {
            trimbuf[i] = inbuf[i];
        }
        baos.write(trimbuf);
    }

所以现在我尝试使用 InputStream 加载文件

    ByteArrayOutputStream out = new ByteArrayOutputStream();
    InputStream is2 = new ByteArrayInputStream(baos.toByteArray());
    JarInputStream in = new JarInputStream(is2);

这在这一点上运作良好。现在我想将这个 InputStream "is2" 加载到 bukkit 服务器中。

因为 class 加载方式在 java 中有效,您不能像您那样直接从输入流加载 class。您可以做的是:

  • 将 jar 中的每个文件加载到内存中
  • 使用自定义 class加载器从内存中加载文件
  • 使用那个 classloader 加载你的插件

第一步很简单,我们将为来自 jar 文件的文件制作 Map<String,byte[]>

Map<String,byte[]> map = new HashMap<>();
ZipEntry entry;
byte[] read = new byte[1024];
while((entry = in.getNextEntry()) != null) {
    ByteArrayOutputStream r = new ByteArrayOutputStream(Math.max(128, entry.getSize()));
    int i;
    while((i = in.read(read) >= 0)
        r.write(read, 0, i);
    is.close();
    map.put(entry.getName(), r.toByteArray());
}

有了我们的文件名映射 -> 数据字节数组,我们就可以实现我们的自定义 class 加载器:

public class MappedJarClassLoader extends ClassLoader {
     Map<String,byte[]> map = new HashMap<>();

     public MappedJarClassLoader (ClassLoader parent, Map<String,byte[]> map) {
         super(parent);
         this.map = map;
     }

     public Class findClass(String name) throws ClassNotFoundException {
         byte[] b = map.get(name.replace('/', '$').replace('.', File.separatorChar));
         if(b == null)
             throw new ClassNotFoundException(name);
         return defineClass(name, b, 0, b.length);
     }
}

由于 bukkit 的限制,我们不能将 class 扩展 JavaPlugin 放在我们加密的 jar 部分中。不要这样做,而是将 class 放入具有接受主实例的构造函数的加密 jar 中,并使用以下方法在 onEnable 中加载 class:

mappedJarClassLoader.loadClass("path.to.new.class").getConstructor(getClass()).newInstance(this)