3

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.

4

2 回答 2

1

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>
于 2013-08-25T18:46:47.493 回答
0

Well, Shiro bind SecurityManager as ThreadLocal via ShiroFilter. Look at the doFilterInternal method. The Subject is bind to ThreadContext only inside subject.execute method, which means the SecurityUtils.getSubject() will return null each time it will be called outside subject.execute, which means outside ShiroFilter, which means outside request thread and this is the problem i think.

AbstractShiroFilter.java

protected void doFilterInternal(ServletRequest servletRequest, ServletResponse servletResponse, final FilterChain chain)
            throws ServletException, IOException {
...

            final Subject subject = createSubject(request, response);

            //noinspection unchecked
            subject.execute(new Callable() {
                public Object call() throws Exception {
                    updateSessionLastAccessTime(request, response);
                    executeChain(request, response, chain);
                    return null;
                }
            });    
...
    } 

Well, you said you inject Stateless B whats your DI framework? Shiro can integrate with Guice and Spring quite easily.

于 2013-08-20T17:48:00.157 回答