web-dev-qa-db-fra.com

Java: pas de gestionnaire de sécurité: chargeur de classe RMI désactivé

Salut, j'ai une application RMI et maintenant j'essaie d'invoquer certaines méthodes sur le serveur depuis mon client. J'ai le code suivant:

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();
    }
}

le serveur est correctement chargé, mais lorsque j'essaie d'appeler server.registerOHLCProvider(provider); j'obtiens ces erreurs:

     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

J'ai ajouté mon fichier de stratégie en tant qu'argument VM, voici à quoi il ressemble:

grant {
    permission Java.security.AllPermission;
}

Il continue de dire quelque chose sur le chargement de classe désactivé, donc je suppose que le problème est quelque part là-bas ... Merci!

26
Xorty

Le chargement de classe à distance peut être délicat.

Le message d'origine ne contient aucune information sur la base de code. Il se peut que la configuration de sécurité du client soit correcte, mais il n'a pas accès au code distant. Les classes sont chargées directement à partir de la "base de code" par le client. Ils ne sont pas présentés au client par le service via la connexion RMI. Le service fait simplement référence à une source externe pour les classes.

Le serveur doit spécifier la propriété système Java.rmi.server.codebase. La valeur doit être une URL accessible au client à partir de laquelle les classes nécessaires peuvent être chargées. S'il s'agit d'un file: URL, le système de fichiers doit être accessible au client.

Et l'inverse: si le serveur doit pouvoir charger des classes à partir du client (comme ici), le client doit définir la propriété de base de code sur une URL accessible au serveur.

25
erickson

Chaque fois que vous appelez une méthode sur un proxy dynamique RMI, le MarshalInputStream (qui étend ObjectInputStream pour remplacer resolveClass et resolveProxyClass) délègue à LoaderHandler pour rechercher à 3 endroits le ClassLoader à utiliser:

  1. Le ClassLoader du proxy qui est appelé (techniquement, il utilise un hack appelé latestUserDefinedLoader(): il parcourt la pile, recherchant la première méthode sur la pile qui ne fait pas partie de JRE).
  2. Thread-local contextClassLoader de l'appelant
  3. CodeBase ClassLoader si SecurityManager est activé
    1. Si la propriété système Java.rmi.server.useCodebaseOnly=false, Alors le ClassLoader de base de code utilise des URL dans la télécommande Java.rmi.server.codebase. Notez que la valeur par défaut de useCodebaseOnly a changé dans JDK 7u21 afin que la base de code distante ne soit plus utilisée à moins que vous ne la changiez !
    2. Sinon, le ClassLoader de base de code utilise des URL dans le local Java.rmi.server.codebase.

Il y a donc plusieurs raisons possibles pour lesquelles vous obtiendrez un ClassNotFoundException lors de l'appel d'une méthode distante:

  • Si la pile ne contient "aucun gestionnaire de sécurité: chargeur de classe RMI désactivé", assurez-vous de définir un SecurityManager comme décrit par d'autres si vous avez besoin du chargement de classe à distance pour les deux côtés pour obtenir toutes les interfaces distantes et les classes sérialisables.
  • Si vous utilisez le chargement de classe à distance et qu'il a cessé de fonctionner lors de la mise à niveau vers JRE 7u21, définissez -Djava.rmi.server.useCodebaseOnly=true Pour qu'il corresponde au comportement précédent ou définissez -Djava.rmi.server.codebase Sur une liste d'URL séparées par des espaces sur les deux les côtés locaux et éloignés. Et assurez-vous que l'ordinateur peut accéder à ces URL.
  • Si vous utilisez un ClassLoader personnalisé localement dont le chargeur de classe parent définit certains Interfaces distantes, assurez-vous d'appeler Thread.setContextClassLoader(ClassLoader) pour que RMI utilise ce ClassLoader. (C'était mon problème: j'avais un SwingWorker qui se trouvait être planifié sur un thread de travail qui a été créé avant que le contextClassLoader ne soit défini sur EventDispatchThread). Par exemple, A et C appartiennent à votre ClassLoader personnalisé mais B appartient au ClassLoader parent, puis lorsque vous appelez a.getB (). GetC (), l'appel getB () utilise le chargeur de classe personnalisé, mais l'appel getC () échouera à trouver C dans le dernierUserDefinedClassLoader et devra se replier sur le contextClassLoader.

Tout cela est un récit édifiant sur la mauvaise conception d'API d'ObjectInputStream. ObjectInputStream aurait dû vous obliger à passer un paramètre ClassLoader, et non pas à en trouver un au hasard à l'aide de latestUserDefinedLoader, contextClassLoader et codebase.

7
yonran

Vous avez besoin du gestionnaire de sécurité côté serveur, pas seulement côté client.

Sans cela, le moteur RMI du serveur refuse de charger des classes à partir du client, car il ne peut garantir que celles-ci ne feront rien de mal sur le serveur.

Avez-vous besoin du chargement de classe RMI? Le serveur ne peut-il pas déjà avoir les classes que le client essaie d'envoyer?

4
Paŭlo Ebermann