# RMI server in einem Mehrbenutzer-System mit Auth?



## sidex (25. Mai 2007)

Hallo

ich brauch hilfe 

Ich habe mit dem RMI-Plugin für Eclipse nun einen kleinen Server hinbekommen. Ich muss jedoch das Ganze für mehrere Clients machen. Aber ich finde nirgendwo Lernmaterial dazu 

Der benutzer Muss sich anmelden Können und ich muss auf dem Server die Clients ientifizieren können um zu sehen welcher Benutzer was darf und was er nicht darf.

Verwaltet denn RMI mehere Clients automatisch? wenn ja müssen dann die Methoden synchronized sein und wie kann ich die Clients identifizieren? wenn nein könnte mir jemanden einen Link zum passenden Lernmaterial geben? 

DANKE im voraus.


----------



## semi (26. Mai 2007)

Schau dir die JSSE Beispiele von Sun an.
http://java.sun.com/javase/6/docs/technotes/guides/security/jsse/samples/index.html


----------



## sidex (28. Mai 2007)

hi

also den oberen link hatte ich davor auch. bringt mir aber ziemlich wenig. ich sehe da gar nichts für mehrere clients.

also ich habe es jetzt wie folgt geregelt. ich hab einen remote-objekt der prüft die benutzer daten. wenn diese korrekt sind dann exportiere ich ein neues remote-objekt mit den ganzen funktionen für jeden benutzer neu (unter dem namen des benutzers.)

so:


```
public int sumbmitValidation(String name, String pass)
			throws RemoteException 
	{
		System.out.println(name + " " + pass);
		
		if ( name.equals("admin") && pass.equals("admin"))
		{
			try
			{	
				AdminCommonImpl ac = new AdminCommonImpl();
				UnicastRemoteObject.exportObject(ac);
				Naming.rebind(name, ac);
				return 0;
			}
			catch(Exception e)
			{
				e.printStackTrace();
			}
		}
		
		return -1;
	}
```

der client verbindet sich dann je nach rückgabewert neu.


```
package rmi.client;

import java.rmi.RMISecurityManager;
import java.rmi.Naming;
import rmi.common.*;

public class RemoteClient {
	
	static AdminCommon ac = null;
	
	/**
	 * @param args
	 */
	public static void main(String[] args) 
	{
		
		final String NAME = "admin";
		final String PASS = "admin";
		final int STATUS;
		
		try
		{
			System.setSecurityManager(new RMISecurityManager());
			RemoteCommon rc = (RemoteCommon) Naming.lookup("rmi://localhost/cianbookserver");
			
			STATUS = rc.sumbmitValidation(NAME, PASS);
			
			if(STATUS != -1)
			{
				ac = (AdminCommon) Naming.lookup("rmi://localhost/" + NAME);
			}
			ac.addPerson("boris");
			System.out.println("Job :" + STATUS);
		}
		catch (Exception e)
		{
			e.printStackTrace();
		}
	}

}
```

jetzt habe ich einige fragen offen:

ist denn das ganze sicher genug? wenn ein client sich mit lookup verbunden hat, kann sich dann ein zweiter client unter dem gleichen namen verbinden? also ich meine, können sich so Naming.lookup("rmi://localhost/" + NAME); mehrere clients verbinden? wenn ja, wie kann ich das regeln, dass da höchstens nur ein client angemeldet seien kann.

wie ist es mit dem schreibenden zugriff? regelt rmi das selbstständig oder muss ich das selber regeln, dass nicht zwei clients gleichzeitig eine schreib-methode aufrufen können? wenn ich es selber machen muss, wie realisiere ich das?

bitte helft mir.

danke im voraus.


----------



## Guest (28. Mai 2007)

sidex hat gesagt.:
			
		

> ...wenn ein client sich mit lookup verbunden hat, kann sich dann ein zweiter client unter dem gleichen namen verbinden? also ich meine, können sich so Naming.lookup("rmi://localhost/" + NAME); mehrere clients verbinden? wenn ja, wie kann ich das regeln, dass da höchstens nur ein client angemeldet seien kann.


Nicht in der Registry binden, sondern nur exportieren und zurückgeben.



			
				sidex hat gesagt.:
			
		

> wie ist es mit dem schreibenden zugriff? regelt rmi das selbstständig oder muss ich das selber regeln, dass nicht zwei clients gleichzeitig eine schreib-methode aufrufen können? wenn ich es selber machen muss, wie realisiere ich das?


Selbst ist der Mann. Lösung: z.B. Transaktionen mit JDBC.

Sobald du ein Objekt in der Registry unter einem bestimmten Namen bindest,
	
	
	
	





```
Naming.rebind(name, ac);
```
ist es für alle erreichbar.
Schau es dir selbst an
	
	
	
	





```
Registry reg = LocateRegistry.getRegistry(Registry.REGISTRY_PORT);
for(String name : reg.list())
{
   System.out.println(name);
}
```

Du wirst solange herumzappeln und das Rad neu erfinden, bis du feststellst, dass das, was du brauchst, 
ein Application Server ist.


----------



## sidex (28. Mai 2007)

ich glaube ich habe nur den letzten satz verstanden  soll das heißen das geht nicht mit rmi? das kann doch wohl nicht wahr sein... wozu soll dnan rmi gut sein wenn sowas damit nicht geht?


----------



## Guest (29. Mai 2007)

Hier ein einfaches Beispiel. Jede Service-Instanz ist nur einem Client bekannt.
Du kannst die createService() Methode in ServiceProvider mit einem Login und Passwort 
versehen und nur dann ein Service-Objekt zurückgeben, wenn der User bekannt ist, sonst 
irgendeine dramatische Exception werfen.

Service.java
	
	
	
	





```
import java.rmi.Remote;
import java.rmi.RemoteException;

public interface Service extends Remote
{
   void serviceMethod1() throws RemoteException;
   void serviceMethod2() throws RemoteException;
}
```
ServiceImpl.java
	
	
	
	





```
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;

class ServiceImpl extends UnicastRemoteObject implements Service
{
   private static final long serialVersionUID = -4322844685655081758L;

   public ServiceImpl() throws RemoteException
   {
      super();
   }

   public void serviceMethod1() throws RemoteException
   {
      System.out.println("serviceMethod1() invoked");
   }

   public void serviceMethod2() throws RemoteException
   {
      System.out.println("serviceMethod2() invoked");
   }
}
```
ServiceProvider.java
	
	
	
	





```
import java.rmi.Remote;
import java.rmi.RemoteException;

public interface ServiceProvider extends Remote
{
   Service createService() throws RemoteException;
}
```
ServiceProviderImpl.java
	
	
	
	





```
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;

class ServiceProviderImpl extends UnicastRemoteObject implements ServiceProvider
{
   private static final long serialVersionUID = -4352807122970077538L;

   public ServiceProviderImpl() throws RemoteException
   {
      super();
   }

   public Service createService() throws RemoteException
   {
      Service service = new ServiceImpl();
      System.out.println("Service created");
      return service;
   }
}
```
Server.java
	
	
	
	





```
import java.rmi.RMISecurityManager;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;

public class Server
{
   public static void main(String[] args)
   {
      System.setSecurityManager(new RMISecurityManager());
      try
      {
         Registry reg = LocateRegistry.createRegistry(Registry.REGISTRY_PORT);
         ServiceProvider serviceProvider = new ServiceProviderImpl();
         reg.rebind("ServiceProvider", serviceProvider);
         System.out.println("Server is running.");
      }
      catch(Exception e)
      {
         e.printStackTrace();
      }
   }
}
```
Client.java
	
	
	
	





```
public class Client
{
   public static void main(String[] args)
   {
      try
      {
         ServiceProvider serviceProvider = (ServiceProvider)java.rmi.Naming.lookup("rmi://localhost/ServiceProvider");
         Service service = serviceProvider.createService();
         service.serviceMethod1();
         service.serviceMethod2();
      }
      catch(Exception e)
      {
         e.printStackTrace();
      }
   }
}
```


----------



## sidex (30. Mai 2007)

das ist doch auch fast das gleiche was ich habe.

das problem ist ja, ist erst mal irgend ein service in der registry angemeldet dann kann sich jedes client an den binden ohne irgendwelche benutzerdaten.

okay mit ssl ist es dann auch nicht mehr so einfach, aber erst mal muss ich raffen wie ssl mit rmi geht :.-( ich dreh langsam durch mit rmi  :autsch: ich glaub es macht kein sinn. Wie der andere da oben schon gesagt hat, brauch ich glaub ich wirklich ein Application Server, dann hab ich zwar mehr Arbeit vor mir aber dafür hab ich mehr kontrolle :-/


----------



## Tellerrand (30. Mai 2007)

sidex hat gesagt.:
			
		

> das ist doch auch fast das gleiche was ich habe.
> 
> das problem ist ja, ist erst mal irgend ein service in der registry angemeldet dann kann sich jedes client an den binden ohne irgendwelche benutzerdaten.


Beachte das hier nur der ServiceProvider angemeldet wurde, der Service an sich wird nich angemeldet, sondern vom ServiceProvider übergeben.
Man kommt also nur über den ServiceProvider an ein Service Objekt, und eben dieser ServiceProvider kann eine Benutzerauthentifizierung möglich machen.

Jedenfalls sehe ich das in dem Beispiel so


----------



## sidex (30. Mai 2007)

ja  komischer weise habe ich das genau 1 minute bevor du es geschrieben hast auch verstanden  sorry ich bin momentan einfach etwas verpeilt 

edit:

ach ja danke.


----------



## sidex (4. Jun 2007)

ich habe wieder ein Problem. Hier ist nun mein server.

Server:

```
package rmi.server;

import java.rmi.server.UnicastRemoteObject;
import java.rmi.registry.*;
import java.rmi.Naming;
import java.rmi.RemoteException;
import rmi.common.impl.ObjectCommonImpl;
import rmi.common.ObjectCommon;
import rmi.common.RemoteProvider;
import java.rmi.RMISecurityManager;
import rmi.ssl.*;
import config.*;

public class RemoteServer extends UnicastRemoteObject 
	implements RemoteProvider {
	
	public RemoteServer() throws Exception 
	{
		super(ConfigData.PORT,
			  new RMISSLClientSocketFactory(),
			  new RMISSLServerSocketFactory());
	}
	
	public ObjectCommon sumbmitValidation(String name, String pass)
	throws RemoteException 
	{
		System.out.println(name + " " + pass);

		if ( name.equals("admin") && pass.equals("admin"))
		{
			try
			{	
				ObjectCommon oc = new ObjectCommonImpl();
				return oc;
			}
			catch(Exception e)
			{
				e.printStackTrace();
			}
		}

		return null;
	}
	
	/**
	 * @param args
	 */
	public static void main(String[] args) 
	{
		
		if (System.getSecurityManager() == null) {
		    System.setSecurityManager(new RMISecurityManager());
		}
		
		try
		{
			
			Registry registry = LocateRegistry.createRegistry(
					ConfigData.PORT,
					new RMISSLClientSocketFactory(),
					new RMISSLServerSocketFactory());
			RemoteServer rs = new RemoteServer();
			Naming.rebind("cianbookserver", rs);
			System.out.println("Server gestartet...");
		}
		catch(Exception e)
		{
			System.out.println("Serverstart Exception");
			e.printStackTrace();
		}
	}

}
```

wenn ich es jedoch ausführen will dann krieg ich folgende nested Exception:



> Serverstart Exception
> java.rmi.ConnectException: Connection refused to host: 127.0.0.1; nested exception is:
> java.net.ConnectException: Connection refused
> at sun.rmi.transport.tcp.TCPEndpoint.newSocket(TCPEndpoint.java:601)
> ...



das ist fast das gleiche wie aus dem rmi beispiel von sun (link siehe oben)

ich seher da nichts mehr was falsch seien könnte. kann mir bitte einer helfen?

danke im voraus.

mfg sidex


----------



## semi (4. Jun 2007)

1) Erstelle zuerst mal irgendeine Keystore Datei
z.B. mit 

```
keytool -genkey -alias localhost -keypass geheim -validity 1000 -keystore keystore -storepass geheim -dname "CN=,OU=,O=,L=,S=,C="
```

2) Ändere den Code im Konstruktor von RMISSLServerSocketFactory


```
try
{
   char[] keyStorePassword = System.getProperty("javax.net.ssl.trustStorePassword").toCharArray();
   KeyStore keyStore = KeyStore.getInstance("JKS");
   keyStore.load(new FileInputStream(System.getProperty("javax.net.ssl.trustStore")), keyStorePassword);
   KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
   kmf.init(keyStore, keyStorePassword);
   SSLContext sslContext = SSLContext.getInstance("TLS");
   sslContext.init(kmf.getKeyManagers(), null, null);
   serverSocketFactory = sslContext.getServerSocketFactory();
}
catch (Exception e)
{
   e.printStackTrace();
   throw e;
}
```

3) SSLServer

```
public class SSLServer
{
   public static void main(String[] args)
   {
      System.setSecurityManager(new RMISecurityManager());
      try
      {
         RMIClientSocketFactory csf = new RMISSLClientSocketFactory(); 
         RMIServerSocketFactory ssf = new RMISSLServerSocketFactory();
         Registry reg = LocateRegistry.createRegistry( Registry.REGISTRY_PORT, csf, ssf );
         final ServiceProvider serviceProvider = new ServiceProviderImpl( 0, csf, ssf );
         reg.rebind("ServiceProvider", serviceProvider);
         System.out.println("Server is running.");
      }
      catch(Exception e)
      {
         e.printStackTrace();
      }
   }
}
```

4) SSLClient


```
public class SSLClient
{
   public static void main(String[] args)
   {
      System.setSecurityManager(new RMISecurityManager());
      try
      {
         Registry reg = LocateRegistry.getRegistry( InetAddress.getLocalHost().getHostAddress(), 
                  Registry.REGISTRY_PORT, new RMISSLClientSocketFactory());          
         
         ServiceProvider serviceProvider = (ServiceProvider)reg.lookup("ServiceProvider");
         Service service = serviceProvider.createService();
         service.serviceMethod1();
         service.serviceMethod2();
      }
      catch(Exception e)
      {
         e.printStackTrace();
      }
   }
}
```

5) ServiceProviderImpl


```
class ServiceProviderImpl extends UnicastRemoteObject implements ServiceProvider
{
   private static final long serialVersionUID = -4352807122970077538L;
   
   private transient RMIClientSocketFactory clientSocketFactory;
   private transient RMIServerSocketFactory serverSocketFactory;
   private transient int serverPort;

   public ServiceProviderImpl() throws RemoteException
   {
      super();
   }
   
   public ServiceProviderImpl(int serverPort, RMIClientSocketFactory clientSocketFactory, 
            RMIServerSocketFactory serverSocketFactory) throws RemoteException
   {
      super(serverPort, clientSocketFactory, serverSocketFactory);
      this.clientSocketFactory = clientSocketFactory;
      this.serverSocketFactory = serverSocketFactory;
      this.serverPort = serverPort;
   }
   
   public Service createService() throws RemoteException
   {
      Service service = new ServiceImpl(serverPort, clientSocketFactory, serverSocketFactory);
      System.out.println("Service created");
      return service;
   }
   
}
```

6) ServiceImpl (wie gehabt, nur ein zusätzlicher Kontruktor)


```
public ServiceImpl(int serverPort, RMIClientSocketFactory clientSocketFactory, 
            RMIServerSocketFactory serverSocketFactory) throws RemoteException
{
   super(serverPort, clientSocketFactory, serverSocketFactory);
}
```


7) Policy-Datei (hier Anarchie, alles erlaubt)


```
grant
{
   permission java.security.AllPermission;
};
```

8) Server und Client starten

```
java -Djava.security.policy=./etc/policy -Djavax.net.ssl.trustStore=./etc/keystore -Djavax.net.ssl.trustStorePassword=geheim SSLServer

java -Djava.security.policy=./etc/policy -Djavax.net.ssl.trustStore=./etc/keystore -Djavax.net.ssl.trustStorePassword=geheim SSLClient
```


----------

