Вопрос

Я адаптирую небольшое клиент-серверное приложение RMI.Я написал несколько вещей:

HelloInterface -> A Hello World interface for RMI
Server -> The server app'
Client -> The client app'

Ничего особенного, но...Я приложил руку к новому RMISecurityManager, который вызывает метод JNI и проверяет разрешения для отдельного пользователя:

package rmi;
import java.rmi.RMISecurityManager;
import java.io.*;

public class NativeRMISecurityManager extends RMISecurityManager
{
    private boolean unix;
    protected static ThreadLocal user = new ThreadLocal();

    /*
     * On interdit l'utilisation du constructeur par defaut
     * pour obliger l'utilisation du constructeur avec user. 
     */
    private NativeRMISecurityManager()
    {
        super();
    }

    public NativeRMISecurityManager (final String user)
    {
        super();
        String OS = System.getProperty("os.name").toLowerCase();
        unix = (OS.compareTo("windows") != 0); /* Assume that if not 
                            * windows, then "UNIX/POSIX" 
                            */
        /*
         * ThreadLocal's user : Each thread is considered 
         * to have different access rights to the machine
         */

        NativeRMISecurityManager.user.set(user);

        if (!unix)
        {
            System.out.println("Systeme : "+OS);
        }
    }

    public void checkRead(String file)
    {
        super.checkRead(file);
        /*
         * If we are on a **IX platform we want to check that 
         * the _user_ has access rights.
         */
        if (unix)
        {
            String str_user = (String)NativeRMISecurityManager.user.get();

            if (file == null)
            {
                throw new SecurityException("file = NULL !!!");
            }
        if (str_user == null)
            {
                throw new SecurityException("user = NULL in the ThreadLocal!!!");
            }

            int ret = c_checkRead(
                    file, 
                    str_user
                    );
            if (ret != 0)
            {
                throw new SecurityException("Access error: " + file);
            }
        }
    }

    public native int c_checkRead(String file, String user);
}

В классе Сервера я делаю это:

String user = "my_user";
System.setSecurityManager(new NativeRMISecurityManager(user));

Кажется, этот класс работает в основном потоке Сервера.Теперь проблема в том, что я пытаюсь подключиться к этому классу сервера и просмотреть реестр.Я получаю это исключение:

Exception in thread "RMI TCP Connection(1)-192.168.42.207"     java.lang.ExceptionInInitializerError
        at sun.rmi.transport.StreamRemoteCall.getInputStream(StreamRemoteCall.java:111)
        at sun.rmi.transport.Transport.serviceCall(Transport.java:118)
        at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:466)
        at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:707)
        at java.lang.Thread.run(Thread.java:595)
Caused by: java.lang.SecurityException: user = NULL dans le ThreadLocal!!!
        at rmi.NativeRMISecurityManager.checkRead(NativeRMISecurityManager.java:62)
        at java.io.File.exists(File.java:700)
        at java.lang.ClassLoader$3.run(ClassLoader.java:1689)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.lang.ClassLoader.loadLibrary0(ClassLoader.java:1686)
        at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1668)
        at java.lang.Runtime.loadLibrary0(Runtime.java:822)
        at java.lang.System.loadLibrary(System.java:993)
        at sun.security.action.LoadLibraryAction.run(LoadLibraryAction.java:50)
        at java.security.AccessController.doPrivileged(Native Method)
        at sun.rmi.server.MarshalInputStream.<clinit>(MarshalInputStream.java:97)
        ... 5 more

ИМХО, смысл этого в том, что поток (неявно) создается и получает NativeRMISecurityManager в качестве SecurityManager по умолчанию.

Может ли кто-нибудь дать совет по этому поводу?

Это было полезно?

Решение

  

ИМХО смысл этого в том, что поток (неявно) создан и получает NativeRMISecurityManager в качестве SecurityManager по умолчанию.

Это правда, хотя это не является причиной вашей ошибки; проблема связана с использованием ThreadLocal. Ключевое свойство ThreadLocal заключается в том, что каждый вызывающий поток имеет свое собственное значение. В этом случае «пользователь» устанавливается (и для) потоком, который инициализирует NativeRMISecurityManager и устанавливает его в качестве диспетчера безопасности системы (предположительно, основного потока).

Однако какой-то другой поток (по-видимому, поток обработки сообщений RMI) вызывает checkRead () - но " пользователь " поле никогда не было установлено для этой темы! Таким образом, значение возвращается как ноль.

Если нет какой-либо причины, чтобы разные потоки имели разные значения - и я не могу выделить одно из вашего примера - я бы рекомендовал сделать " пользователь " поле a) строка, а не ThreadLocal и б) нестатическая. Это должно решить вашу проблему с нулевым значением.

Тем не менее, предполагая, что это ограничение требования / дизайна, проблема с текущей архитектурой заключается в том, что только поток, который создает экземпляр NativeRMISecurityManager, фактически получает пользователя - каждый другой поток становится нулевым.

Если углубиться в это немного глубже, я думаю, что мне нужно лучше понять вашу проблемную область, чтобы предложить какие-либо полезные предложения по исправлению. Кроме того, нет ничего быстрого или грязного в архитектуре безопасности Java. Однако я сделаю все возможное, работая с несколькими предположениями:

<Ол>
  • Потоки, созданные системой, считаются доверенными
  • Темы, созданные вашим кодом, должны указывать пользователя
  • Дочерние потоки должны наследовать пользователя создаваемого потока
  • Потенциальная реализация:

    public class NativeRMISecurityManager extends RMISecurityManager {
    
        private static final boolean UNIX;
    
        static {
            String OS = System.getProperty("os.name").toLowerCase();
            UNIX = (OS.compareTo("windows") != 0); /* Assume that if not 
                                                    * windows, then "UNIX/POSIX" 
                                                    */
        }
    
        protected static InheritableThreadLocal<String> user =
            new InheritableThreadLocal<String>();
    
        public static setThreadUser(String username) {
            user.set(username);
        }
    
    
        public NativeRMISecurityManager(String initialUser) {
            super();
            // Set the user for the thread that constructs the security manager
            // All threads created as a child of that thread will inherit the user
            // All threads not created as a child of that thread will have a 'null' user
            setThreadUser(initialUser);
        }
    
    
        public void checkRead(String file) {
            super.checkRead(file);
            /*
             * If we are on a **IX platform we want to check that 
             * the _user_ has access rights.
             */
            if (UNIX)
            {
                if (file == null)
                {
                    throw new SecurityException("file = NULL !!!");
                }
    
                String str_user = NativeRMISecurityManager.user.get();
    
                if (str_user != null)
                {
                    // Note: sanitize input to native method
                    int ret = c_checkRead(file, str_user);
    
                    if (ret != 0)
                    {
                        throw new SecurityException("Access error: " + file);
                    }
                }
    
                // Assume a system thread and allow access
            }
        }
    
        public native int c_checkRead(String file, String user);
    }
    

    Другие советы

    Да !Вот и все.

    Я подумал об этом вчера днем ​​и пришел к тому же решению.Я опубликую здесь свой код для тех, кому это будет интересно.1) Nativermisecurity Manager 2) C -код C (вы должны генерировать .h с Javah

    (примечание:Я не буду переводить это на английский, так как здесь много комментариев на французском)

    package rmi;
    
    import java.rmi.RMISecurityManager;
    
    /**
     * <p> Ce SecurityManager, qui herite de RMISecurityManager,
     * implemente une verification supplementaire des droits
     * d'acces aux fichiers.
     * A la creation du SecurityManager et lors de la creation
     * de nouveaux threads, on renseigne ThreadLocal du nom du
     * _user_ du thread.
     * <p>Ainsi, lors des checkRead() et checkWrite()
     * notre SecurityManager appelle une methode native (JNI)
     * qui va verifier directement si le user a les droits 
     * d'acces a la ressource. 
     * <p><b>Warning : NE PAS OUBLIER DE FAIRE APPEL A 
     * setCurrentUser() DANS CHAQUE THREAD CREE.</b>
     * <p> <b>Remarque :</b> Pour les informations sur la compilation 
     * et l'execution de la lib ecrite en C, cf. le fichier README. 
     * @author a_po
     */
    public class NativeRMISecurityManager extends RMISecurityManager
    {
        private boolean unix;
        protected ThreadLocal user = new ThreadLocal();
    
        /**
         * Constructeur par defaut.
         * <p><b>ATTENTION :</b> Bien faire appel a la methode setCurrentUser(String) !
         * Sinon le SecurityManager se comportera comme un RMISecurityManager classique.
         * @see public void setCurrentUser(String userName)
         */
        public NativeRMISecurityManager()
        {
            super();
            String OS = System.getProperty("os.name").toLowerCase();
            unix = (OS.compareTo("windows") != 0); /* Si le systeme 
                                                        * n'EST PAS windows, 
                                                        * alors c'est UNIX...
                                                        * 
                                                        * Pas tres rigoureux,
                                                        * mais sinon il faut tester
                                                        * Systeme V, Linux, *BSD,
                                                        * Sun OS, ...
                                                        */
    
            /*
             * User du ThreadLocal : Chaque thread est considere comme ayant des
             * droits d'acces au systeme potentiellement differents.
             */
            this.user.set(user);
    
            if (!unix)
            {
                System.out.println("Systeme : "+OS);
            }
        }
    
    
        /**
         * Verification en lecture.
         * <p>
         * Dans le cas ou l'on est sur une plateforme POSIX,
         * on souhaite verifier que le _user_ du Thread a le droit
         * de lecture sur le fichier.
         * <p>
         * De plus, dans le cas ou user est null, cela signifie
         * OBLIGATOIREMENT que le thread a ete cree "automatiquement"
         * et que le thread courant n'est pas un thread de "tache a executer".
     * <p>
         * En effet, le user est recupere dans le ThreadLocal
         * et on force l'initialisation de cette variable a l'instanciation
         * du SecurityManager (en mettant le constructeur par defaut prive) ou
         * en faisant appel a setCurrentUser(String)
         * @see void rmi.NativeRMISecurityManager.setCurrentUser(String user)
         */
        public void checkRead(String file)
        {
            super.checkRead(file);
    
            String str_user = (String)this.user.get();
    
            if (unix && str_user != null)
            {
                if (file == null)
                {
                    throw new SecurityException("file = NULL !!!");
                }
    
                int ret = c_checkRead(file, str_user);
                if (ret != 0)
                {
                    throw new SecurityException("Erreur d'acces au fichier : "     + file);
                }
            }
        }
    
        /**
         * Verification d'acces en ecriture sur un fichier.
         * @see void rmi.NativeRMISecurityManager.checkRead(String file)
         */
        public void checkWrite(String file)
        {
            super.checkWrite(file);
            String str_user = (String)this.user.get();
    
            if (unix && str_user != null)
            {
                if (file == null)
                {
                    throw new SecurityException("file = NULL !!!");
                }
    
                int ret = c_checkWrite(file, str_user);
                if (ret != 0)
                {
                    throw new SecurityException("Erreur d'acces au fichier : "         + file);
                }
            }
        }
    
        /**
         * Configure le thread courant pour que le user soit pris en compte
         * dans les verifications d'acces aux fichiers.
         * @param user
         */
        public void setCurrentUser(String userName)
        {
            this.user = new ThreadLocal();
            this.user.set(userName);
        }
    
        public String getCurrentUser()
        {
            if (user!=null){
                return (String)user.get();
            }
            else return null;
        }
    
        /**
         * Methode native a implementer en C.
         * @param file
         * @param user
         * @return 0 si ok <p> -1 sinon
         */
        public native int c_checkRead(String file, String user);
    
        /**
         * Idem que pour c_checkRead
         * @param file
         * @param user
         * @return
         * @see int rmi.NativeRMISecurityManager.c_checkRead(String file, String user)
         */
        public native int c_checkWrite(String file, String user);
    
        /**
         * Chargement de la bibliotheque JNI.
         */
        static
        {
            System.loadLibrary("rmi_NativeRMISecurityManager");
        }
    }
    

    И библиотека C:

    #include <stdio.h>
    #include <jni.h>
    #include <sys/stat.h>
    #include <sys/types.h>
    #include <unistd.h>
    #include <pwd.h>
    #include <stdlib.h>
    #include <grp.h>
    #include <string.h>
    #include "rmi_NativeRMISecurityManager.h"
    
    /* Droits en lecture / ecriture / execution */
    
    #define R_RIGHT 4
    #define X_RIGHT 1
    #define W_RIGHT 2
    
    JNIEXPORT jint JNICALL Java_rmi_NativeRMISecurityManager_c_1checkRead
      (JNIEnv *env, jobject obj, jstring file, jstring user)
    {
        int ret = check_permission(env, obj, file, user);
        /**
         * La permission d'acces a un fichier vaut ceci :
         * 1 pour l'execution
         * 2 pour l'ecriture
         * 4 pour la lecture.
         * Donc :
         * * Droit en lecture : 4, 5, 6, 7
         * * Droit en ecriture : 2, 3, 6, 7
         * * Droit en execution : 1, 3, 5, 7.
         */
        if (ret == R_RIGHT || ret == R_RIGHT + W_RIGHT || 
            ret == R_RIGHT + X_RIGHT || ret == R_RIGHT + W_RIGHT + X_RIGHT)
        {
            return 0;
        }
        else
            return -1;
    }
    
    JNIEXPORT jint JNICALL Java_rmi_NativeRMISecurityManager_c_1checkWrite
      (JNIEnv *env, jobject obj, jstring file, jstring user)
    {
        int ret = check_permission(env, obj, file, user);
        /**
         * La permission d'acces a un fichier vaut ceci :
         * 1 pour l'execution
         * 2 pour l'ecriture
         * 4 pour la lecture.
         * Donc :
         * * Droit en lecture : 4, 5, 6, 7
         * * Droit en ecriture : 2, 3, 6, 7
         * * Droit en execution : 1, 3, 5, 7.
         */
        if (ret == W_RIGHT || ret == W_RIGHT + R_RIGHT || 
            ret == W_RIGHT + X_RIGHT || ret == W_RIGHT + R_RIGHT + X_RIGHT)
        {
            return 0;
        }
        else
            return -1;
    }
    
    
    int check_permission(JNIEnv *env, jobject obj, jstring file, jstring user)
    {
        struct stat pstat;
        const char* pzcfile = (*env)->GetStringUTFChars(env, file, 0);
        const char* pzcuser = (*env)->GetStringUTFChars(env, user, 0);
        struct passwd* puserInfo;
        int bisOwner = 0;
        int bisGroup = 0;
        struct group* pgroupInfo;
        int i;
        int droits = 0;
    
        /* recuperer les informations relatives au fichier */
        if(lstat(pzcfile, &pstat)<0)
        {
            fprintf(stderr,"* Le fichier %s n'exite pas.\n", pzcfile);
            (*env)->ReleaseStringUTFChars(env, file, pzcfile);
            (*env)->ReleaseStringUTFChars(env, user, pzcuser);
            return -1;
        }
    
        /* recuperer l'identifiant du user */
        puserInfo = getpwnam(pzcuser);
        if(puserInfo == NULL)
        {
            fprintf(stderr,"* L'utilisateur %s n'est pas connu du systeme.\n", pzcuser);
            (*env)->ReleaseStringUTFChars(env, file, pzcfile);
            (*env)->ReleaseStringUTFChars(env, user, pzcuser);
            return -2;
        }
    
        /* regarder si le user est proprietaire du fichier */
        if(puserInfo->pw_uid == pstat.st_uid)
        {
            bisOwner = 1;
        }
        /* si le user n'est pas proprietaire, verifier s'il est membre du groupe */
        if(!bisOwner)
        {
            /* recuperer les informations relatives au groupe */
            pgroupInfo = getgrgid(pstat.st_gid);
            /* parcourir la liste des membres du groupe a la recherche du user */
            for(i=0;;i++)
            {
                if(pgroupInfo->gr_mem[i] == NULL)
                {
                    break;
                }
                if(strcmp(pgroupInfo->gr_mem[i],pzcuser) == 0)
                {
                    bisGroup = 1;
                    break;
                }
            }
        }
    
        /* recuperer les droits correspondants au user */
        if(bisOwner)
        {
            droits = (pstat.st_mode & S_IRWXU) >> 6;
        }
        else if(bisGroup)
        {
            droits = (pstat.st_mode & S_IRWXG) >> 3;
        }
        else
        {
            droits = pstat.st_mode & S_IRWXO;
        }
    
        /* liberer les espaces memoire alloues */
        (*env)->ReleaseStringUTFChars(env, file, pzcfile);
        (*env)->ReleaseStringUTFChars(env, user, pzcuser);
        return droits;
    }
    

    Большое спасибо, Грег Кейс.Это меня утешает, потому что мы нашли одно и то же решение.:)

    Лицензировано под: CC-BY-SA с атрибуция
    Не связан с StackOverflow
    scroll top