Search code examples
jakarta-eejsf-2jboss7.xjava-ee-6shiro

Shiro in Java EE 6 application


I was following BalusC code to manage user authentication in Java EE 6 (http://balusc.blogspot.com/2013/01/apache-shiro-is-it-ready-for-java-ee-6.html) which works great if I stay on the same web container.

I'm facing an issue that perhaps anyone can help me. When injecting an EJB that resides in the Web container, the SecurityUtils.getSubject() works ok from any method of that EJB.

The problem is when I try to do it on an injected EJB from another container (even an ejb jar in the same EAR).

The error I get is:

Caused by: org.apache.shiro.UnavailableSecurityManagerException: No SecurityManager accessible to the calling code, either bound to the org.apache.shiro.util.ThreadContext or as a vm static singleton. This is an invalid application configuration.

The use case is:

Managed bean A with injected stateless session bean B. Class A resides in myApp.war, class B resides in myApp.ejb, both inside myApp.ear. I am calling SecurityUtils from class B.

Do you have any clues on how to solve this?

I am running JSF 2, Java EE 6, JBoss 7.1.


Solution

  • I'm answering the question myself.

    I solved the integration using a Filter and a custom LoginModule for JBoss:

    The filter (it must be applied after the invocation of shiro filter):

    public class ShiroJAASIntegrationFilter implements Filter{
    
        static Logger logger = Logger.getLogger(ShiroJAASIntegrationFilter.class);
        @Override
        public void destroy() {
        }
    
        @Override
        public void doFilter(ServletRequest arg0, ServletResponse arg1,
                FilterChain arg2) throws IOException, ServletException {
            HttpServletRequest httpServletRequest = (HttpServletRequest)arg0;
            Principal userPrincipal = httpServletRequest.getUserPrincipal();
            HttpSession session = httpServletRequest.getSession(false);
            if(userPrincipal!=null){
                if(session!= null && session.getAttribute("shiroAuthenticated")==null){
                    String name = userPrincipal.getName();
                    try {
                        httpServletRequest.login(name,"");
                        session.setAttribute("shiroAuthenticated",true);
                    } catch (ServletException e) {
                        logger.debug("Unable to authenticate user" + e.getMessage());
                    }
                }
    
            }
            arg2.doFilter(arg0, arg1);
        }
    
        @Override
        public void init(FilterConfig arg0) throws ServletException {
        }
    }
    

    The loginmodule, it just sets the user as authenticated:

    public class ShiroJAASLoginModule extends UsernamePasswordLoginModule {
    
        /**
         * Always return true. 
         */
        protected boolean validatePassword(String inputPassword,
                String expectedPassword) {
            return true;
        }
    
        protected Group[] getRoleSets() throws LoginException {
            //TODO: if needed, add roles
            Group[] roleSets = { new SimpleGroup("Roles") };
            roleSets[0].addMember(new SimplePrincipal(getUsername()));
            return roleSets;
        }
    
    
        @Override
        protected String getUsersPassword() throws LoginException {
            return null;
        }
    
    }
    

    The module must be defined in standalone.xml:

    <security-domain name="shiro" cache-type="default">
     <authentication>
      <login-module code="web.security.shiroJaasIntegration.ShiroJAASLoginModule" flag="required"/>
     </authentication>
    </security-domain>
    

    Finally,define the security-domain in your apps jboss-web.xml:

    <jboss-web>
        <security-domain>shiro</security-domain>
    
    </jboss-web>