You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 09:13:57