使用AccessController限制权限:Java插件权限管控问题咨询
阻止Java内嵌插件访问受限资源的解决方案
我完全懂你的困扰——Java传统安全模型(Policy、AccessController、ProtectionDomain)确实高度依赖JAR包的代码来源来划分权限,要给主应用内部的插件做细粒度的权限隔离,得绕开默认的域绑定逻辑,手动给插件代码分配受限的ProtectionDomain。
先说说你现有代码的问题:你创建了空权限的ProtectionDomain,但没有把插件的代码和这个域关联起来。默认情况下,类是由它的类加载器+代码来源(比如JAR路径)自动分配域的,所以你需要给插件单独用自定义类加载器加载,并在加载时指定我们的受限域。
下面是具体的实现步骤和代码示例:
1. 自定义Policy,根据ProtectionDomain返回权限
首先不能用空的Policy,我们需要让Policy根据不同的域返回对应的权限——主应用的域保留全权限,插件的域只给允许的权限:
class PluginPolicy : Policy() { private val appPermissions = Permissions().apply { // 给主应用分配全权限,或者按需添加具体权限 add(AllPermission()) } private val pluginPermissions = Permissions().apply { // 只给插件允许的权限,比如只读访问特定目录 add(FilePermission("/allowed/plugin/dir/-", "read")) // 拒绝其他所有权限,这里可以按需调整 } override fun getPermissions(domain: ProtectionDomain): Permissions { // 判断当前域是不是我们给插件分配的受限域 return if (domain.permissions == pluginPermissions) { pluginPermissions } else { appPermissions } } override fun refresh() {} }
2. 用自定义类加载器加载插件,并绑定受限ProtectionDomain
我们需要创建一个类加载器,加载插件类时手动指定我们的受限域,而不是用默认的域:
class PluginClassLoader(parent: ClassLoader, private val restrictedDomain: ProtectionDomain) : ClassLoader(parent) { @Throws(ClassNotFoundException::class) fun loadPluginClass(className: String, classBytes: ByteArray): Class<*> { // 从字节数组加载类,并指定我们的受限ProtectionDomain return defineClass(className, classBytes, 0, classBytes.size, restrictedDomain) } }
3. 整合逻辑,初始化安全管理器并加载插件
现在把这些部分整合起来,初始化安全管理器,加载插件并限制它的权限:
fun main(args: Array<String>) { // 设置自定义Policy val pluginPermissions = Permissions().apply { add(FilePermission("/allowed/plugin/dir/-", "read")) } val restrictedDomain = ProtectionDomain(null, pluginPermissions) Policy.setPolicy(PluginPolicy()) // 启用安全管理器 System.setSecurityManager(SecurityManager()) // 模拟加载插件:比如从文件读取插件类的字节码 val pluginClassBytes = Files.readAllBytes(Paths.get("/path/to/plugin/PluginClass.class")) val pluginClassLoader = PluginClassLoader(Thread.currentThread().contextClassLoader, restrictedDomain) val pluginClass = pluginClassLoader.loadPluginClass("com.example.PluginClass", pluginClassBytes) // 执行插件代码,此时插件会受到权限限制 val pluginInstance = pluginClass.getDeclaredConstructor().newInstance() val method = pluginClass.getMethod("doSomething") // 如果插件尝试访问受限资源,这里会抛出SecurityException method.invoke(pluginInstance) }
关键注意点
- SecurityManager的废弃提示:Java 17及以后版本中SecurityManager已被标记为废弃,如果你的项目用的是新版本Java,可能需要考虑替代方案,比如用Java模块系统的
--add-opens/--add-exports做细粒度的模块访问控制,或者用沙箱框架(比如ByteBuddy做方法级拦截)。 - 最小必要权限原则:一定要根据插件的实际需求分配最小必要权限,避免过度授权。比如如果插件只需要读取特定文件,就只给对应的FilePermission,不要给AllPermission。
- 类加载器隔离:用自定义类加载器不仅可以绑定权限域,还能实现插件和主应用的类隔离,避免类冲突。
内容的提问来源于stack exchange,提问作者Max
相关产品推荐
相关产品推荐

