The ClassLoader needs to provide a ProtectionDomain
identifying the source of the code:
public class PluginClassLoader extends ClassLoader {
private final ClassProvider provider;
private final ProtectionDomain pd;
public PluginClassLoader(ClassProvider provider) {
this.provider = provider;
Permissions permissions = new Permissions();
this.pd = new ProtectionDomain(provider.getCodeSource(), permissions, this, null);
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
byte[] classDef = provider.getClass(name);
Class<?> clazz = defineClass(name, classDef, 0, classDef.length, pd);
return clazz;
}
}
By overriding findClass
rather than loadClass
the delegational model is preserved, and the PluginClassLoader will first query the system and parent classloader for class definitions.
Create a Policy:
public class PluginSecurityPolicy extends Policy {
private final Permissions appPermissions = new Permissions();
private final Permissions pluginPermissions = new Permissions();
public PluginSecurityPolicy() {
// amend this as appropriate
appPermissions.add(new AllPermission());
// add any permissions plugins should have to pluginPermissions
}
@Override
public Provider getProvider() {
return super.getProvider();
}
@Override
public String getType() {
return super.getType();
}
@Override
public Parameters getParameters() {
return super.getParameters();
}
@Override
public PermissionCollection getPermissions(CodeSource codesource) {
return new Permissions();
}
@Override
public PermissionCollection getPermissions(ProtectionDomain domain) {
return isPlugin(domain)?pluginPermissions:appPermissions;
}
private boolean isPlugin(ProtectionDomain pd){
return pd.getClassLoader() instanceof PluginClassLoader;
}
}
Finally, set the policy and a SecurityManager (default implementation is fine):
Policy.setPolicy(new PluginSecurityPolicy());
System.setSecurityManager(new SecurityManager());