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
