Wie setzt man SecurityContext um SessionContext.getCallerPricipal in bean aufzurufen

Jep, dass mit dem eigenen Login-Modul war ja eh meine Idee. Ich habe mir aber ein LoginModul von Grundauf neu gebastelt. D.h. das implementiert nur javax.security.auth.spi.LoginModul und erweitert noch kein AbstractServerLoginModule. Dazu noch eingenen CallBacks und einen CallBackHandler, aber dann bekomme ich halt diese EJBAccessException.

Ich versuche jetzt mal, wie du vorgeschlagen hast, was mit der JBoss-Admin-Doku hinzubekommen.
 
So, neuer Lösungsansatz:

Eigene JBoss-LoginInitialContextFactory implementiert, damit beim erstellen des InitialContext mit meinem LoginModul und mit meinem CallBackHandler gearbeitet wird:

Java:
@Override
	public Context getInitialContext(Hashtable env) throws NamingException {

		String protocol = env.get(Context.SECURITY_PROTOCOL).toString();
		// Hol noch eigene Properties aus der env

		try {
			MyCallbackHandler handler = new MyCallbackHandler(/* übergebe die eigenen Properties aus env */);
			
			LoginContext lc = new LoginContext(protocol, handler);
			lc.login();
		} catch (LoginException e) {
			AuthenticationException ex = new AuthenticationException(
					"Failed to login using protocol=" + protocol);
			ex.setRootCause(e);
			throw ex;
		}

		Context iniCtx = super.getInitialContext(env);
		return iniCtx;

	}

Am Client:
Java:
Properties properties = new Properties();
		properties.put(Context.INITIAL_CONTEXT_FACTORY, "de.kwyjibo.factory.MyLoginInitialContextFactory");
		properties.put(Context.URL_PKG_PREFIXES,
				"=org.jboss.naming:org.jnp.interfaces");
		properties.put(Context.PROVIDER_URL, "localhost:1099");
		properties.put(/* noch weitere eigens definierte Properties*/);
		properties.put(Context.SECURITY_PROTOCOL, "MySecurityDomain");

		Context context = new InitialContext(properties);

Bis dahin läuft alles so, wie ich das will. Context wird erstellt und der login wird überprüft und alles ist ok.

Dann kommt der Lookup:
Java:
IMyBeanRemote bean = (IMyBeanRemote) context.lookup("MyBean/remote");
bean.doSth();

Dann wird zwar mein LoginModul verwendet aber in der initialize-Methode wird auf der Serverseite ein Objekt vom Typ org.jboss.security.auth.callback.SecurityAssociationHandler als CallbackHandler übergeben anstatt mein eigenen CallBackHandler.

Meine Vermutung ist jetzt, dass ich einen eigenes ServerLoginModul brauch. Oder gibt es eine Möglichkeit, dem JBoss zu sagen, dass er mein CallBackHandler benutzt. Das würde ja aber noch nicht reichen, da ich ja den CallBackHandler mit ein paar Daten aus der InitialContext env initialisieren müsste.

Hätte mein LoginModul auf der Serverseite auch meinen CallBackHandler würde alles funktionieren. Vielleicht fehlt mir nur noch ein kleiner Schritt.

Hilfe ist sehr willkommen!
Grüße,
Sebastian
 
Zuletzt bearbeitet:
Es gibt eine Möglichkeit der Serverseite (JBoss) den selbst geschriebenen CallBackHandler unterzujubeln.

in der MBean (jboss-service.xml):
Code:
<!-- JAAS security manager and realm mapping -->  
<mbean code="org.jboss.security.plugins.JaasSecurityManagerService"  
   name="jboss.security:service=JaasSecurityManager">  
...

kann man noch den Eintrag
Code:
<attribute name="CallbackHandlerClassName">  
   de.kwyjibo.MyCallBackHandler  
</attribute>

hinzufügen. Dabei ist zu beachten, dass JBoss die nötigen Klassen ins lib Verzeichnis gelegt werden müssen. Liegen sie im Projekt das Deployt wird, gibt es eine ClassNotFoundException, da die MBeans eingelesen werden bevor die Projekte geladen werden.

Das habe ich aber noch nicht ausprobiert.

Das wird mir aber eh wenig helfen, da ich meinem CallBackHandler ja im Konstruktor den Benutzername und noch weitere Informationen mitgebe und wenn der Server meinen CallBackHandler initialisiert würde er ja nur den Standardkonstruktor nutzen.
 
Hallo,

ich bin bisher davon ausgegangen, dass du die zusätzlichen Informationen für deinen User erst auf der Serverseite hinzufügts. Wenn du das schon auf Clientseite machst, ist es natürlich noch einfacher.

Du brauchst nur einen eigenen Principal. Als Beispiel hier der CustomPrincipal:
Java:
package de.test.login;

import java.io.Serializable;
import java.security.Principal;

public class CustomPrincipal implements Principal,Serializable{

	private String name;
	private String info;
	
	public CustomPrincipal(String name){
		this.name=name;
	}
	
	public String getInfo() {
		return info;
	}

       public void setInfo(String info) {
		this.info = info;
	}

	public int hashCode(){
	      return name.hashCode();
	 }

        public boolean equals(Object obj){
	      Principal p = (Principal) obj;
	      return name.equals(p.getName());
	 }

	 public String toString(){
	      return name;
	 }

        public String getName(){
	      return name;
	 }
}
Wichtig hier - der Principal muss Serializable implementieren, damit er auf die Serverseite transportiert werden kann. Jetzt packst du den Principal in ein jar File und legst ihn im lib Verzeichnis auf den Server ab, damit deine EJB bzw. der Server auch was mit der neuen Principalklasse anfangen können.

Als nächstes kommt die Bean:
Java:
package de.test.login.ejb;

import java.security.Principal;

import javax.annotation.Resource;
import javax.annotation.security.RolesAllowed;
import javax.ejb.EJBContext;
import javax.ejb.Remote;
import javax.ejb.Stateless;

import org.jboss.annotation.security.SecurityDomain;

import de.test.login.CustomPrincipal;

@SecurityDomain("mydomain")
@Stateless
@Remote
public class BusinessBean implements Business {

	@Resource
	private EJBContext ctx;
	
	public String helloWorld() {
		System.out.println(ctx.getCallerPrincipal().getName());
		System.out.println(ctx.getCallerPrincipal().getClass());
		Principal pr = ctx.getCallerPrincipal();
		if(pr instanceof CustomPrincipal){
			System.out.println(((CustomPrincipal) pr).getInfo());
		}
		return "Hello World";
	}

}
Den Businessinterface habe ich mal weggelassen. Die Bean soll auf die Principalklasse zugreifen können. Jetzt muss noch eine entsprechende Security-Domain in der login-conf.xml definiert werden:
Code:
     <application-policy name = "mydomain">
       <authentication>
          <login-module code = "org.jboss.security.ClientLoginModule"
             flag = "required">
          </login-module>
       </authentication>
    </application-policy>
Die Domain benutzt einfach nur das ClientLoginModule, damit werden die vom Client übergebenen Werte einfach nur weiterbenutzt - es erfolgt keine Authentifizierung bzw. Authorisierung.

Und zum Schluss der Client:
Java:
package de.test.login;

import java.util.Properties;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;

import de.test.login.ejb.Business;

public class Client {
	public static void main(String[] args) throws NamingException {
		Properties properties = new Properties();
		
		CustomPrincipal princ = new CustomPrincipal("username");
		princ.setInfo("Alles ganz einfach");
		
		properties.put(Context.INITIAL_CONTEXT_FACTORY, "org.jboss.security.jndi.JndiLoginInitialContextFactory");
		properties.put(Context.URL_PKG_PREFIXES, "=org.jboss.naming:org.jnp.interfaces");
		properties.put(Context.PROVIDER_URL, "jnp://localhost:1099");
		properties.put(Context.SECURITY_PRINCIPAL, princ);
		properties.put(Context.SECURITY_CREDENTIALS, "theduke");
		
		Context context = new InitialContext(properties);
		Business busi = (Business) context.lookup("BusinessBean/remote");
		System.out.println(busi.helloWorld());
	}
}
Der Client benutzt die JndiLoginInitialContextFactory und übergibt als Umgebungseintrag einfach einen erzeugten CustomPrincipal. Aus dem Server wird beim Aufruf der helloWorld() Methode dann die Info aus dem CustomPrincipal in die Console ausgegeben

Grüße
THMD
 
Ha, da war ich so nah dran, grumml :rolleyes:

JndiLoginInitialContextFactory
die lässt mein Principal in Ruhe und packt es nicht aus und macht kein SimplePrincipal draus. Wunderbar.

ClientLoginModule
auf der Serverseitigen Login-Config... ok, jetzt funktionierts

Vielen Dank THMD
 
Zuletzt bearbeitet:
Zurück