You are viewing a plain text version of this content. The canonical link for it is here.
Posted to user@shiro.apache.org by Piero Sartini <li...@pierosartini.de> on 2011/07/15 00:41:25 UTC
shiro and clustered EJB applications
Hello List.
I am developing an EJB application and decided to use Shiro for our
authentication and authorization needs.
There are some questions I do not understand completeley - any help is
much appreciated.
1. I guess SecurityUtil.getSubject() can't work correctly if we are
using multiple JVMs.
How is it possible to authenticate cluster-wide? If the user gets
authenticated in Method A running on instance1 - Method B running on
instance2 will not know about this user. Even if it is the same
request.. right?
2. I am using EnterpriseCacheSessionDAO and implemented my own
CacheManager using Hazelcast.
Is this stuff used if sessionStorageEnabled = false? I do not see any
keys created inside Hazelcast now. But I really do not want Sessions
that live longer than one request... the app should be completely
stateless.
3. If the only way is to enable sessionStorage - how does instance2
know which session ID to retrieve from the DAO?
Do I need to come up with my own solution (using EJB context or
something similar)?
Way too much questions :-)
-ps
Re: shiro and clustered EJB applications
Posted by Piero Sartini <ps...@sartini-its.com>.
Hi Les.
Thanks again for your excellent explanations.
> I guess if EJB limits you like that, then you'd have to jump through
> some hoops (but it is still possible - see below). And while EJB may
> not allow this, I'd be shocked if there isn't a container-specific way
> of supporting this (I think JBoss Remoting can support this if you're
> using JBoss for example).
Yeah - I was a bit surprised as well. But it seems when it comes to
remote calls,
there is a lot of flexibility missing. I've read about client
interceptors in JBoss,
but our AS of choice is glassfish.
While your suggested solution seems doable, I am not sure we will go with it.
Need to play around with some code, but in the end it could be easier
to just implement
a custom jaas realm in combination with some home-grown AuthService.
Thanks again.
-ps
Re: shiro and clustered EJB applications
Posted by Les Hazlewood <lh...@apache.org>.
I guess if EJB limits you like that, then you'd have to jump through
some hoops (but it is still possible - see below). And while EJB may
not allow this, I'd be shocked if there isn't a container-specific way
of supporting this (I think JBoss Remoting can support this if you're
using JBoss for example).
However, if you want to remain container independent and EJB does not
support the notion of user-supplied invocation metadata, I know of a
work around that I have used on a project before: I wrote a little
framework that creates AOP proxies that implement each Service
interface. The client code calls these proxies. Each one of the
proxies in turn calls a a 'MethodInvocationDispatcher' EJB remote
proxy. That MethodInvocationDispatcher EJB on the server side
discovers what 'real' EJB and method should being called, and invokes
that local bean/method with the arguments. This is essentially an
implementation of the Front Controller design pattern.
For example, you could create an EJB, with a single method signature:
RemoteMethodInvocationResult invoke(RemoteMethodInvocation invocation);
This essentially creates a very simple/small application-level
remoting mechanism that sits on top of EJB remoting. When I created
mine, it didn't take long - maybe a day or two to work the kinks out.
But because you would control the objects that are sent across the
wire, you could leverage it for any other type of framework stuff you
may need that EJB doesn't support.
The RemoteMethodInvocationResult and RemoteMethodInvocation objects
are yours - you can create them however you wish. So, I would ensure
that a method existed: remoteMethodInvocation.getAttributes() :
Map<String,Object>. This way you could populate the attribute map
with whatever metadata that you wanted for your framework needs. For
supporting Shiro, in a stateful app, one of those 'attributes' would
be a session id, or for a stateless app, you'd send along the
PrincipalCollection and authenticated state (stateless app).
Once your server-side EJB receives the invocation, you'd essentially
do the same thing that the SecureRemoteInvocationExecutor does in
Shiro's Spring support.
HTH!
Best,
--
Les Hazlewood
CTO, Katasoft | http://www.katasoft.com | 888.391.5282
twitter: http://twitter.com/lhazlewood
katasoft blog: http://www.katasoft.com/blogs/lhazlewood
personal blog: http://leshazlewood.com
Re: shiro and clustered EJB applications
Posted by Piero Sartini <ps...@sartini-its.com>.
> Thanks a lot, this is exactly what I was searching for.
> Now I only need to find out how to intercept remote calls from the
> servlet to the EJBs and attach some context information.
> Not sure if CDI/EJB interceptors are able to handle this... from what
> I know they come to play before calling the method on the server that
> hosts the EJB.
After a lot of searching it seems that there is no easy way to attach
context information to a remote EJB call.
Interceptors run on the same server where the EJB is located, not on the client.
Does this mean Shiro is not useable in an (remote) EJB context?
-ps
Re: shiro and clustered EJB applications
Posted by Piero Sartini <ps...@sartini-its.com>.
Hi Les!
Thanks for your detailed answer.
> This is because once a Subject logs-in, Shiro will by default store
> the Subject's authentication state in the session, and so if the
> Session is accessible to both Shiro environments across JVMs, this
> will work as expected. I call this 'Poor Man's Single Sign On' and
> I've used in a few applications now. It works great.
>
> The key to this is to use the same SessionDAO in both JVM
> configurations. Most people use Ehcache and/or Terracotta to ensure
> this works in a distributed environment. See
> http://shiro.apache.org/session-management.html#SessionManagement-SessionStorage
> for more.
>
> This however does require server state to be maintained (e.g. in
> Hazelcast) and so therefore is not a stateless solution.
In this scenario, how does jvm2 query for an existing session - created by jvm1?
If we do not talk about web applications and do not already have
distributed servlet sessions - how does Shiro know which user is
authenticated in the current (ejb) request?
How is a returning user identified?
>> 3. If the only way is to enable sessionStorage - how does instance2
>> know which session ID to retrieve from the DAO?
>> Do I need to come up with my own solution (using EJB context or
>> something similar)?
>
> Based on what you've described thus far, I'm assuming you have an
> architecture similar to the following:
>
> Load Balancer --> Servlet Container --> Load Balancer --> EJB Server
> --> Data Store
It is more like:
Load Balancer --> Servlet Container --> EJB (clustered)
and
Message Queue (clustered) --> EJB (clustered)
Right now Servlet Container and EJBs are on the same machine, inside
one EAR - but we want to be able to split them out in the future.
There are 3 nodes in the cluster running this EAR.
> And you'd like the same Subject state to be available in the Servlet
> Container as well as the EJB Server for a single request down to the
> datastore and back. Because you want a truly stateless architecture,
> you do not want shared sessions to be used, and this also implies that
> your clients (browser, app, whatever) will authenticate on every
> request since you don't want to store authentication state in a
> session.
Exactly.
> Shiro can actually support this use case very well assuming you are
> using the 1.2 SNAPSHOT since stateless app support is currently in SVN
> trunk.
We already use 1.2-SNAPSHOT because of a bug in 1.1.0 that prevents
the use of a JNDI Realm (JndiRealmFactory).
-> https://issues.apache.org/jira/browse/SHIRO-238
> 2. Create some framework code to do the following:
>
> a. When JVM 1 sends a remote invocation to JVM 2 (e.g. servlet
> container invokes an EJB proxy), you will want to attach the
> authentication state (subject.isAuthenticated() and the
> PrincipalCollection (subject.getPrincipals()) to the remote method
> invocation payload.
>
> b. In the EJB Server, have an interceptor that intercepts all remote
> method invocations. This interceptor will inspect the RMI payload,
> extract out the 'authenticated' boolean and the PrincipalCollection
> and create a Subject instance based on those two values. For example:
>
> boolean authenticated = //get from RMI payload
> PrincipalCollection principals = //get from RMI payload
>
> Subject subject =
> Subject.Builder(ejbApplicationShiroSecurityManager).principals(principals).authenticated(authenticated).buildSubject();
>
> return subject.getExecute( new Callable() {
> public Object call() throws Exception {
> return ejbMethodInvocation.invoke(...);
> }
> }
>
> This will create and bind the Subject state 'passed in' from the
> servlet container to the EJB server for further use. Any object
> invoked during 'ejbMethodInvocation.invoke()' - the EJB, other EJBs,
> DAOs, etc, will all be able to call SecurityUtils.getSubject() just
> fine.
>
> Here is a working example that does this for Spring remoting, but
> would work identically for EJB remoting:
>
> http://svn.apache.org/repos/asf/shiro/trunk/support/spring/src/main/java/org/apache/shiro/spring/remoting/SecureRemoteInvocationExecutor.java
>
> The above steps (with the RMI interceptor) will work just fine, and it
> will still be stateless.
Thanks a lot, this is exactly what I was searching for.
Now I only need to find out how to intercept remote calls from the
servlet to the EJBs and attach some context information.
Not sure if CDI/EJB interceptors are able to handle this... from what
I know they come to play before calling the method on the server that
hosts the EJB.
I've found a post that tries to solve this problem - will investigate
this later.
-> http://home.java.net/node/707280
-ps
Re: shiro and clustered EJB applications
Posted by Les Hazlewood <lh...@apache.org>.
> return subject.getExecute( new Callable() {
Oops. That should be:
subject.execute( new Callable() {
...
Cheers,
Les
Re: shiro and clustered EJB applications
Posted by Les Hazlewood <lh...@apache.org>.
On Thu, Jul 14, 2011 at 3:41 PM, Piero Sartini <li...@pierosartini.de> wrote:
> Hello List.
Hi Piero! Welcome to the list.
> I am developing an EJB application and decided to use Shiro for our
> authentication and authorization needs.
> There are some questions I do not understand completeley - any help is
> much appreciated.
>
> 1. I guess SecurityUtil.getSubject() can't work correctly if we are
> using multiple JVMs.
> How is it possible to authenticate cluster-wide? If the user gets
> authenticated in Method A running on instance1 - Method B running on
> instance2 will not know about this user. Even if it is the same
> request.. right?
Actually, this will work just fine if JVM1 and JVM2 have their own
Shiro environment (SecurityManager) and are configured to point to the
same Session data store. This requires you to use Shiro's native
session management (and not, say, the Servlet Container's session
management if you're deploying a web application).
This is because once a Subject logs-in, Shiro will by default store
the Subject's authentication state in the session, and so if the
Session is accessible to both Shiro environments across JVMs, this
will work as expected. I call this 'Poor Man's Single Sign On' and
I've used in a few applications now. It works great.
The key to this is to use the same SessionDAO in both JVM
configurations. Most people use Ehcache and/or Terracotta to ensure
this works in a distributed environment. See
http://shiro.apache.org/session-management.html#SessionManagement-SessionStorage
for more.
This however does require server state to be maintained (e.g. in
Hazelcast) and so therefore is not a stateless solution.
> 2. I am using EnterpriseCacheSessionDAO and implemented my own
> CacheManager using Hazelcast.
> Is this stuff used if sessionStorageEnabled = false? I do not see any
> keys created inside Hazelcast now. But I really do not want Sessions
> that live longer than one request... the app should be completely
> stateless.
If sessionStorageEnabled = false, then you're telling Shiro not to use
a Subject's session for Shiro's own needs, which includes storing
authentication state. So Shiro would never create a new Session and
you wouldn't see any Sessions in Hazelcast unless you created any
yourself by explicitly calling subject.getSession() or
request.getSession().
> 3. If the only way is to enable sessionStorage - how does instance2
> know which session ID to retrieve from the DAO?
> Do I need to come up with my own solution (using EJB context or
> something similar)?
Based on what you've described thus far, I'm assuming you have an
architecture similar to the following:
Load Balancer --> Servlet Container --> Load Balancer --> EJB Server
--> Data Store
And you'd like the same Subject state to be available in the Servlet
Container as well as the EJB Server for a single request down to the
datastore and back. Because you want a truly stateless architecture,
you do not want shared sessions to be used, and this also implies that
your clients (browser, app, whatever) will authenticate on every
request since you don't want to store authentication state in a
session.
Shiro can actually support this use case very well assuming you are
using the 1.2 SNAPSHOT since stateless app support is currently in SVN
trunk.
1. Set sessionStorageEnabled = false, which will disable Shiro from
using Sessions to store data for all requests. Note that this does
not prevent you from creating sessions, so be careful not to call
subject.getSession() yourself if you don't want sessions to be
created. You can use the 'noSessionCreation' servlet filter to
enforce this in a webapp.
2. Create some framework code to do the following:
a. When JVM 1 sends a remote invocation to JVM 2 (e.g. servlet
container invokes an EJB proxy), you will want to attach the
authentication state (subject.isAuthenticated() and the
PrincipalCollection (subject.getPrincipals()) to the remote method
invocation payload.
b. In the EJB Server, have an interceptor that intercepts all remote
method invocations. This interceptor will inspect the RMI payload,
extract out the 'authenticated' boolean and the PrincipalCollection
and create a Subject instance based on those two values. For example:
boolean authenticated = //get from RMI payload
PrincipalCollection principals = //get from RMI payload
Subject subject =
Subject.Builder(ejbApplicationShiroSecurityManager).principals(principals).authenticated(authenticated).buildSubject();
return subject.getExecute( new Callable() {
public Object call() throws Exception {
return ejbMethodInvocation.invoke(...);
}
}
This will create and bind the Subject state 'passed in' from the
servlet container to the EJB server for further use. Any object
invoked during 'ejbMethodInvocation.invoke()' - the EJB, other EJBs,
DAOs, etc, will all be able to call SecurityUtils.getSubject() just
fine.
Here is a working example that does this for Spring remoting, but
would work identically for EJB remoting:
http://svn.apache.org/repos/asf/shiro/trunk/support/spring/src/main/java/org/apache/shiro/spring/remoting/SecureRemoteInvocationExecutor.java
The above steps (with the RMI interceptor) will work just fine, and it
will still be stateless.
HTH!
Best regards,
--
Les Hazlewood
CTO, Katasoft | http://www.katasoft.com | 888.391.5282
twitter: http://twitter.com/lhazlewood
katasoft blog: http://www.katasoft.com/blogs/lhazlewood
personal blog: http://leshazlewood.com