Java:没有安全管理器:RMI类加载器被禁用

时间:2011-06-12 13:07:58

标签: java networking remoting rmi securitymanager

您好我有RMI应用程序,现在我尝试从我的客户端调用服务器上的一些方法。我有以下代码:

public static void main(final String[] args) {
    try {
        //Setting the security manager

        System.setSecurityManager(new RMISecurityManager());
        IndicatorsService server = (IndicatorsService) Naming
                .lookup("rmi://localhost/" + IndicatorsService.SERVICE_NAME);
        DataProvider provider = new OHLCProvider(server);
        server.registerOHLCProvider(provider);
    } catch (MalformedURLException e) {
        e.printStackTrace();
    } catch (RemoteException e) {
        e.printStackTrace();
    } catch (NotBoundException e) {
        e.printStackTrace();
    }
}

服务器已正确加载,但当我尝试呼叫server.registerOHLCProvider(provider);时,我收到以下错误:

     java.rmi.ServerException: RemoteException occurred in server thread; nested exception is: 
    java.rmi.UnmarshalException: error unmarshalling arguments; nested exception is: 
    java.lang.ClassNotFoundException: sk.xorty.client.providers.OHLCProvider (no security manager: RMI class loader disabled)
    at sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:336)
    at sun.rmi.transport.Transport$1.run(Transport.java:159)
    at java.security.AccessController.doPrivileged(Native Method)
    at sun.rmi.transport.Transport.serviceCall(Transport.java:155)
    at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:535)
    at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:790)
    at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:649)
    at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
    at java.lang.Thread.run(Thread.java:662)
    at sun.rmi.transport.StreamRemoteCall.exceptionReceivedFromServer(StreamRemoteCall.java:255)
    at sun.rmi.transport.StreamRemoteCall.executeCall(StreamRemoteCall.java:233)
    at sun.rmi.server.UnicastRef.invoke(UnicastRef.java:142)
    at sk.fri.statistics.service.impl.IndicatorsServiceImpl_Stub.registerOHLCProvider(Unknown Source)
    at sk.fri.statistics.service.Client.main(Client.java:61)
Caused by: java.rmi.UnmarshalException: error unmarshalling arguments; nested exception is: 
    java.lang.ClassNotFoundException: sk.xorty.client.providers.OHLCProvider (no security manager: RMI class loader disabled)
    at sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:296)
    at sun.rmi.transport.Transport$1.run(Transport.java:159)
    at java.security.AccessController.doPrivileged(Native Method)
    at sun.rmi.transport.Transport.serviceCall(Transport.java:155)
    at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:535)
    at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:790)
    at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:649)
    at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
    at java.lang.Thread.run(Thread.java:662)
Caused by: java.lang.ClassNotFoundException: sk.xorty.client.providers.OHLCProvider (no security manager: RMI class loader disabled)
    at sun.rmi.server.LoaderHandler.loadClass(LoaderHandler.java:375)
    at sun.rmi.server.LoaderHandler.loadClass(LoaderHandler.java:165)
    at java.rmi.server.RMIClassLoader$2.loadClass(RMIClassLoader.java:620)
    at java.rmi.server.RMIClassLoader.loadClass(RMIClassLoader.java:247)
    at sun.rmi.server.MarshalInputStream.resolveClass(MarshalInputStream.java:197)
    at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1574)
    at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1495)
    at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1731)
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1328)
    at java.io.ObjectInputStream.readObject(ObjectInputStream.java:350)
    at sun.rmi.server.UnicastRef.unmarshalValue(UnicastRef.java:306)
    at sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:290)
    ... 9 more

我已将我的策略文件添加为VM参数,以下是它的样子:

grant {
    permission java.security.AllPermission;
}

它一直在说有关禁用类加载的内容,所以我猜问题就在那里...... 谢谢!

6 个答案:

答案 0 :(得分:25)

远程类加载可能很棘手。

原始帖子不包含有关代码库的任何信息。可能是客户端的安全配置正确,但它无法访问远程代码。这些类由客户端直接从“代码库”加载。它们不是由服务通过RMI连接呈现给客户端的。该服务仅引用类的外部源。

服务器应指定系统属性java.rmi.server.codebase。该值必须是客户端可以访问的URL ,可以从中加载必要的类。如果这是file:网址,则客户端必须可以访问文件系统

反过来说:如果服务器应该能够从客户端加载类(如此处),则客户端必须将代码库属性设置为服务器可访问的URL。

答案 1 :(得分:7)

每次在RMI动态代理上调用方法时,MarshalInputStream(扩展ObjectInputStream以覆盖resolveClassresolveProxyClass)委托给LoaderHandler查看3个位置以供ClassLoader使用:

  1. 正在调用的代理的ClassLoader(从技术上讲,它使用一个名为latestUserDefinedLoader()的hack:它向上移动堆栈,寻找堆栈中不属于JRE的第一个方法。)
  2. 来电者的线程本地contextClassLoader
  3. 如果启用了SecurityManager,则为Codebase ClassLoader
    1. 如果系统属性java.rmi.server.useCodebaseOnly=false,则代码库ClassLoader使用远程 java.rmi.server.codebase中的网址。请注意default value of useCodebaseOnly changed in JDK 7u21 so that remote codebase is not used anymore unless you change it
    2. 否则,代码库ClassLoader使用本地 java.rmi.server.codebase中的网址。
  4. 因此,在调用Remote方法时,有几个可能的原因可以获得ClassNotFoundException

    • 如果堆栈包含“没有安全管理器:禁用RMI类加载器”,那么如果需要双方进行远程类加载以获取所有远程接口和可序列化类,请确保按照其他人的描述设置SecurityManager。
    • 如果您正在使用远程类加载并且在升级到JRE 7u21时它停止工作,则设置-Djava.rmi.server.useCodebaseOnly=true以匹配先前的行为,或将-Djava.rmi.server.codebase设置为以空格分隔的URL列表本地和偏远双方。并确保计算机可以访问这些URL。
    • 如果您在本地使用自定义ClassLoader,其父类加载器定义某些远程接口,请确保调用Thread.setContextClassLoader(ClassLoader)以便RMI将使用该ClassLoader。 (这是我的问题:我有一个SwingWorker碰巧被安排到在EventDispatchThread上设置contextClassLoader之前创建的工作线程上。例如,A和C属于您的自定义ClassLoader,但B属于父ClassLoader,那么当您调用a.getB()。getC()时,getB()调用将使用自定义类加载器,但getC()调用将无法在latestUserDefinedClassLoader中找到C并且必须回退到contextClassLoader。

    所有这些都是关于ObjectInputStream API设计不佳的警示故事。 ObjectInputStream应该要求你传递一个ClassLoader参数,而不是尝试使用latestUserDefinedLoader,contextClassLoader和codebase来随意找到一个。

答案 2 :(得分:7)

我想添加一些可能对某些人特别是初学者有用的东西。 我来到这里寻找上述错误的解决方案,但是作为一个初学者,我不知道如何使用安全策略并指定“ java.rmi.server.codebase”属性。 解决该错误的最简单方法是确保要通过RMI发送的对象的类位于包的相同路径中。通过这种方式,两个应用程序相对于其主文件夹和文件夹在同一位置具有该类。错误会解决。

示例: 如果要从服务器向客户端发送MedicationDTO类型的对象(可序列化),请确保该对象位于同一包路径中。 在我的情况下,在服务器应用程序中,对象位于com.example.springdemo.dto中,而在客户端应用程序中,对象位于com.example.springdemo.service.dto中。问题是使用IntelliJ,因为service包其中没有任何内容,但是有一个其他程序包,它们的名称已串联(service.dto),我看不到路径不相同。 因此,请确保您的类具有相同的包路径。 (对于我的情况,解决方案:MedicationDTO类必须在软件包com.example.springdemo.dto的两个应用程序中同时存在。

我知道这不是最好的解决方案,这只是一个“小技巧”,但是那时我会很高兴找到这个解决方案,因为它使我免于浪费大量时间来解决问题。 / p>

我希望这对希望快速修复该错误的人有所帮助,因为我认为学习使用安全管理器并包括代码库可能会有些棘手,并且需要时间。

答案 3 :(得分:4)

您需要服务器端的安全管理器,而不仅仅是客户端。

如果没有这个,服务器的RMI引擎拒绝从客户端加载类,因为它不能保证这些不会在服务器上做坏事。

您是否需要加载RMI类?服务器是否已经拥有客户端尝试发送的类?

答案 4 :(得分:0)

我知道我的情况非常特殊,但也许它可以帮助其他人。我曾经遇到过,一台服务器上有多个应用程序共享同一个注册表,当然有不同的路径。无论在何处创建此注册表(通常来自第一个应用程序),所有后续应用程序的完整类路径都必须在此第一个应用程序中指定。总结一下:

  1. 确保提供政策
  2. 确保为所有应用程序、客户端和服务器指定完整的类路径

答案 5 :(得分:-3)

我知道为什么会这样。 例如,您在项目A中启动服务器, 但是你使用项目B中的Client来请求这个服务器,这是错误的。 所以你应该将服务器和客户端放在同一个项目中。