On Thu, Jul 14, 2011 at 3:41 PM, Piero Sartini <[email protected]> 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

Reply via email to