-
1. Re: Is it possible to take objects created from a custom LoginModule and retrieve those objects from an EJB or servlet
flossware Sep 4, 2014 2:45 PM (in response to flossware)I finally found and figured out a solution...
So to be fair, I happened upon this article which gave me the idea: http://publib.boulder.ibm.com/infocenter/wsdoc400/v6r0/index.jsp?topic=/com.ibm.websphere.iseries.doc/info/ae/ae/tsec_propcustjavaser.html
Basically what I did was the following:
- I created a serializable (well its Externalizable) object to hold my data being retrieved in the custom LoginModule. For this example we'll call the object LoginContext.
- In the custom LoginModule, at the end of the login() method, I populate the data retrieved into the LoginContext and store it as a private credential using Subject.getPrivateCredentials().add(LoginContext)
- My EJB module has a dependency on the aforementioned classes and use an @SecurityDomain. When retrieving, I simply do PolicyContext.getContext("javax.security.auth.Subject.container").getPrivateCredentials() and then find the object in the returned set whose class is LoginContext.class.
Some "code" may better explain. Let's assume I want to share a Foo and a Bar that I look up in my custom LoginModule:
public class Foo implements Serializable {
// Some methods...
}
public class Bar implements Serializable {
// Some methods
}
// Just using serializable here for brief explanation.
public class LoginContext implements Serializable {
private Foo foo;
private Bar bar;
public void setFoo(final Foo foo) {this.foo = foo;}
public Foo getFoo() {return foo);
public void setBoo(final Foo foo) {this.bar = bar;}
public Bar getBoo() {return bar;}
}
// My custom login module:
public class MyCustomLoginModule implements LoginModule {
private Subject subject;
...
@Override
public void initial(final Sibject subject, final CallbackHandler callbackHandler, final Map sharedState, final Map options) {
...
this.subject = subject;
...
}
@Override
public boolean login() throws LoginException {
...
final Foo foo = ...;
final Bar bar = ...;
final LoginContext = new LoginContext();
loginContext.setFoo(foo);
loginContext.setBar(bar);
subject.getPrivateCredentials().add(loginContext);
...
}
...
}
// My EJB
@Local
public interface LoginContextLocal {
LoginContext getLoginContext();
}
@Stateless
@SecurityDomain("MySecurityDomain")
public class LoginContextBean implements LoginContextLocal {
@PermitAll
@Override
public LoginContext getLoginContext() {
try {
final Set set = ((Subject) PolicyContext.getContext("javax.security.auth.Subject.container")).getPrivateCredentials();
for (final Object privateCreds : set) {
if (LoginContext.class == privateCreds.getClass()) {
return (LoginContext) privateCreds;
}
}
catch (final PolicyContextException policyContextException) {
...
}
return null;
}
}
In maven, I did find I had to include the following dependency in both my project for the custom login module and the ejb as follows:
<dependency>
<groupId>javax.security.jacc</groupId>
<artifactId>javax.security.jacc-api</artifactId>
<version>1.5</version>
</dependency>
I did have some problem retrieving the Subject in the EJB layer if I wasn't using an EJB denoted w/ my security domain - I'd get the following exception: java.lang.IllegalArgumentException: unknown handler key
As a side note, my custom Login Module is an actual JBoss module (aka lives in the $JBOSS_HOME/modules directory). I had to do the following in my EJB layer:
- setup my jboss-deployment-structure.xml to refer to that module.
- denote the dependency as a provided scope in my Maven pom.xml. Not doing so results in ClassCastException's when using the aforementioend LoginContext. I believe separate class loaders are being used which is what causes the ClassCastException.
Above, by using an EJB you can now inject it and use it all over the place