Ok, here we go...
Step 1: use a RealmIdentifierInterceptor like this (I've removed a few quirks
which were specific to our solution):
/**
* This incerceptor intercepts calls to SessionBeans which have been annotated
* appropriately. It determines the principal issuing the request and identifies
* the target realm. It then injects the EntityManager for the chosen realm.
*
* @author Jörg Henne
*/
public class RealmIdentifierInterceptor {
private static final Logger logger = Logger
.getLogger(RealmIdentifierInterceptor.class);
private static final String EM_JNDI_PREFIX = "java:/EntityManagers/";
/**
* A cache of injectors, indexed by class.
*/
private Map<Class, Field> fieldCache = new HashMap<Class, Field>();
@Resource
javax.ejb.SessionContext sctx;
@AroundInvoke
public Object identifyRealm(InvocationContext ctx) throws Exception {
Field field = getFieldForInjection(ctx.getBean().getClass());
if (null != field) {
Principal callerPrincipal = sctx.getCallerPrincipal();
if (null == callerPrincipal)
throw new EJBAccessException(
"No caller principal to detect
the realm name from");
String realmName =
getRealmName(callerPrincipal.getName());
try {
logger.debug("Injecting EM for realm " +
realmName + " during call to "
+ ctx.getBean());
String emName = EM_JNDI_PREFIX + realmName;
EntityManager em = (EntityManager) new
InitialContext().lookup(emName);
field.set(ctx.getBean(), em);
} catch (NamingException e) {
throw new EJBAccessException("Realm " +
realmName + " not found");
}
}
return ctx.proceed();
}
/**
* Extract Realm name from Windows-style realm\\username pattern.
*
* @param principalName
*
* @return
*/
private String getRealmName(String principalName) {
int idx = principalName.indexOf('\\');
if (idx <= 0)
throw new EJBAccessException("Can't parse the principal
name "
+ principalName);
return principalName.substring(0,idx);
}
/**
* Get the injector for the given class. Returns null, if the
* class doesn't declare a field annotated with
*
* @RealmBasedPersistenceContext.
*
* @param clazz
* @return
*/
private Field getFieldForInjection(Class<? extends Object> clazz) {
// a null injector is possible and idicates a class which
declared
// this interceptor, but doesn't declare a field annotated with
// @RealmBasedPersistenceContext.
if (fieldCache.containsKey(clazz))
return fieldCache.get(clazz);
Field[] fields = clazz.getDeclaredFields();
for (int i = 0; i < fields.length; i++) {
Field field = fields[ i ];
RealmBasedPersistenceContext annotation = field
.getAnnotation(RealmBasedPersistenceContext.class);
if (null != annotation) {
logger.info("Found field for EntityManager
injection: " + field
+ " on " +
field.getDeclaringClass());
field.setAccessible(true);
fieldCache.put(clazz, field);
return field;
}
}
logger.warn("Class " + clazz
+ " is annotated with
@RealmIdentifierInterceptor bit doesn't "
+ "declare a field annotated with
@RealmBasedPersistenceContext");
// nothing found - put null map entry to indicate invalid class
fieldCache.put(clazz, null);
return null;
}
}
Step 2: Define the following annotation interface:
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface RealmBasedPersistenceContext {
}
Step 3:
Annotate your session beans with
@Interceptors({RealmIdentifierInterceptor.class})
and your EntityManager field with @RealmBasedPersistenceContext
A simple bean would thus look like this:
@Stateless
@Interceptors({RealmIdentifierInterceptor.class})
public class YadaBean implements Yada {
private static final Logger logger = Logger
.getLogger(AccountAdminDAOBean.class);
@RealmBasedPersistenceContext
private EntityManager em;
...
}
Step 4: Implement a custom login module which is able to deal with principal
names using the pattern realmName/userName.
Our solution is rather complicated in this area, insofar as we subclassed a
specialized Principal from SimplePrincipal which parses the compound
realmName/userName into its separate parts. The customized LoginModule
converts an incoming SimplePrincipal into the specialized one, but clients are
preferred to supply the specialized one directly, if possible.
The problem with this solution is that that the JBoss login mechanism with
ClientLoginModule and the server-side handling silently dump the specialized
Principal instance and replace it with a simple one again. Therefore it is
easier to just stick with SimplePrincipals and deal with the compound principal
name in your LoginModule.
Step 5: Implement an EJB which lists the configured realms from JNDI like this:
@Stateless
public class RealmListServiceBean implements RealmListService {
/*
* (non-Javadoc)
*
* @see de.hess.zas.business.RealmListService#listRealmNames()
*/
public List listRealmNames() throws NamingException {
NamingEnumeration name = new InitialContext()
.list("java:/EntityManagers");
List results = new ArrayList();
while (name.hasMoreElements()) {
results.add(name.next().getName());
}
return results;
}
}
In order to make this work, you have to grant access to this bean to
unauthenticated users, obviously. There are several ways to do this:
Deploy the bean outside the security domain
Except the bean from security
Allow logon using a special guest user (that's our solution)
I hope, this explanation was helpful.
Joerg Henne
View the original post :
http://www.jboss.com/index.html?module=bb&op=viewtopic&p=3952942#3952942
Reply to the post :
http://www.jboss.com/index.html?module=bb&op=posting&mode=reply&p=3952942
Using Tomcat but need to do more? Need to support web services, security?
Get stuff done quickly with pre-integrated technology to make your job easier
Download IBM WebSphere Application Server v.1.0.1 based on Apache Geronimo
http://sel.as-us.falkag.net/sel?cmd=lnk&kid=120709&bid=263057&dat=121642
_______________________________________________
JBoss-user mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/jboss-user