5 Replies Latest reply on Nov 9, 2004 4:16 AM by tschraepen

    Problems with JAAS authentication and Struts/EJB

    kristiane

      Hi,

      I have been trying to implement authentication using JAAS in my Struts based application running on JBoss.
      The application consists of some servlets implemented using Struts, and some session EJB implementing the business logic. Both the
      servlets and EJBs are secured and need a authenticated user to access them.

      The authentication works I that the user is authenticated and the Principal set on logon. But then when I try to make an EJB call
      on the next Struts action, I get Principal=null exception:

      20:15:30,188 ERROR [SecurityInterceptor] Authentication exception, principal=null
      20:15:30,198 ERROR [LogInterceptor] EJBException, causedBy:
      java.lang.SecurityException: Authentication exception, principal=null

      It seems that the Principal and authentication information is lost somewhere between the login action, where the authentication
      occurs, and the next action, where a EJB call is done...

      I have been using the http://www.javaworld.com/javaforums/showflat.php?Cat=2&Board=JavaSecurity&Number=2500&page=0&view=collapsed&sb=5&o=&fpart=1> Complete configuration of JAAS on JBOSS and STRUTS

      The login code looks like this:

      public ActionForward execute(...) {
      
       // Some code removed for simplicity
      
       SecurityAssociationHandler handler = new SecurityAssociationHandler();
       SimplePrincipal user = new SimplePrincipal(j_username);
       handler.setSecurityInfo(user, new String(j_password));
       LoginContext loginContext = new LoginContext("notatbase", (CallbackHandler)handler);
      
       loginContext.login();
       Subject subject = loginContext.getSubject();
      
       session.setAttribute("subject", subject);


      Now I check that the Subject is indeed set, and that I can access the EJB layer with the following code

      logger.debug("Subject: "+SecurityAssociation.getSubject().toString());
      
       UserAdminHome userhome = (UserAdminHome)EjbLookupUtility.lookupHome(UserAdminHome.JNDI_NAME);
       UserAdminRemote userbean = userhome.create();
       userbean.getCurrentUserDTO();


      This code works. The Subject is set, and the EJB call is successful. The following is printed out:

      20:15:29,847 DEBUG [LoginAction] Subject: Subject:
      Principal: ke@objectnet.no
      Principal: Roles(members:Member)
      20:15:29,877 DEBUG [UserAdminEJB] Using caller ke@objectnet.no as current user

      But on the next (Struts) action when the following code is executed the EJB call fails with the above exception.

      public ActionForward execute(...) {
      
       // Some code removed for simplicity
      
       if (SecurityAssociation.getSubject() != null)
       logger.debug("Subject: "+SecurityAssociation.getSubject().toString());
       else
       logger.debug("Subject is null");
      
       UserAdminHome userhome = (UserAdminHome)EjbLookupUtility.lookupHome(UserAdminHome.JNDI_NAME);
       UserAdminRemote userbean = userhome.create();
       userbean.getCurrentUserDTO();
      

      Here the Subject is null, and the userhome.create() call will throw the following execption:

      20:15:30,188 DEBUG [SystemInfoAction] Subject is null

      20:15:30,188 ERROR [SecurityInterceptor] Authentication exception, principal=null
      20:15:30,198 ERROR [LogInterceptor] EJBException, causedBy:
      java.lang.SecurityException: Authentication exception, principal=null

      What am I doing wrong here? Is there a bug in JBoss?
      I have used many days on this problem and have all but given up. If sombody have any ideas, clues or downright solutions,
      please let me know...

      Regards,
      - Chris

      More details:
      I am using JBoss 3.2.4 with Tomcat 5.0. I have also used JBoss 3.2.5 with the same result. To give a more complete picture of my
      implementation I have attached the files necesary to implement the security features in JBoss.

      The application is packed as a EAR with a WAR for the servlets and a JAR for the EJBs. The deployment descriptors looks like the
      following:

      In jboss.xml and jboss-web.xml I have added the following line to secure the servlets and EJBs:

      <security-domain>java:/jaas/notatbase</security-domain>


      In the login-config.xml I have added the following security domain:

      <application-policy name="notatbase">
       <authentication>
       <login-module code="org.jboss.security.ClientLoginModule" flag="required" />
       <login-module code="org.jboss.security.auth.spi.DatabaseServerLoginModule" flag="required">
       <module-option name="managedConnectionFactoryName">jboss.jca:service=LocalTxCM,name=NotatbaseDS</module-option>
       <module-option name="dsJndiName">java:/NotatbaseDS</module-option>
       <module-option name="principalsQuery">Select passwd as Password from users where email=?</module-option>
       <module-option name="rolesQuery">select 'Member' as Role, 'Roles' as RoleGroup from users where email=?</module-option>
       </login-module>
       </authentication>
       </application-policy>


      The ejb-jar.xml is a very long file, generated by XDoclet. This is only the most interesting part:

      <security-role>
       <description>[CDATA[description not supported yet by ejbdoclet]]</description>
       <role-name>Member</role-name>
       </security-role>
       <method-permission >
       <description>[CDATA[description not supported yet by ejbdoclet]]</description>
       <role-name>Member</role-name>
       <method >
       <description>[CDATA[]]</description>
       <ejb-name>UserAdmin</ejb-name>
       <method-intf>Home</method-intf>
       <method-name>create</method-name>
       <method-params>
       </method-params>
       </method>
       </method-permission>
       <method-permission >
       <description>[CDATA[description not supported yet by ejbdoclet]]</description>
       <role-name>Member</role-name>
       <method >
       <description>[CDATA[Gets the UserDTO for the current user.]]</description>
       <ejb-name>UserAdmin</ejb-name>
       <method-intf>Remote</method-intf>
       <method-name>getCurrentUserDTO</method-name>
       <method-params>
       </method-params>
       </method>
       </method-permission>
      

      The web.xml:

      <!-- notatbase, fails in auth-constraint, does not recognize role,
       and is therefore calling <form-login-page> anyway -->
       <security-constraint>
       <web-resource-collection>
       <web-resource-name>notatbase</web-resource-name>
       <description>Security constraint for all resources</description>
       <url-pattern>*.do</url-pattern>
       <http-method>GET</http-method>
       <http-method>POST</http-method>
       </web-resource-collection>
       <!-- the role which can access these resources
       <auth-constraint>
       <role-name>Member</role-name>
       </auth-constraint> -->
       <user-data-constraint>
       <description>no description</description>
       <transport-guarantee>NONE</transport-guarantee>
       </user-data-constraint>
       </security-constraint>
       <login-config>
       <auth-method>FORM</auth-method>
       <form-login-config>
       <form-login-page>/login.do</form-login-page>
       <form-error-page>/logout.do</form-error-page>
       </form-login-config>
       </login-config>
       <security-role>
       <description>A user allowed to invoke Member methods</description>
       <role-name>Member</role-name>
       </security-role>


        • 1. Re: Problems with JAAS authentication and Struts/EJB
          starksm64

          Then something between the point of the LoginContext.login and the SecurityAssociation.getSubject() showing null is clearing the SecurityAssociation. A common source of this is trying to use the RMIAdaptor from jndi in the context of the invocation. The RMIAdaptor cannot be used like this because it clears the caller identity. You need to use the MBeanServer directly if that is what is happening.

          • 2. Re: Problems with JAAS authentication and Struts/EJB
            mthoma

            Hi Scott

            Could you please show a little example for this?

            Thanks
            Martin

            • 3. Re: Problems with JAAS authentication and Struts/EJB
              kristiane

              Hi,

              There are no calls clearing the SecurityAssosiation in my code between LoginContext.login() and SecurityAssociation.getSubject(). I am not using any RMIAdaptor. There might be something in the Struts framework code, but this seems unlikely (using Action and RequestProcessor)

              Could it be as suggested in the topic http://www.jboss.org/index.html?module=bb&op=viewtopic&t=38229:

              The ClientLoginModule places the principals and credentials which are aquired by the previous login module(s) in a magic way to a magic place where bean invocation mechanism passes them to the container resp. beans. I suppose the security information is associated with the Thread object.

              I can confirm that your assumptions are correct. Please note JKuhn that this can lead to suprising effects in your web application, because most servlet containers use thread-pooling ;-). So the next request might behave as not logged in, whereas requests that you are supposing are not logged in, seem to do....


              Comments anyone?


              Anyhows, I found a workaround to the above mentioned problem. In the RequestProcessor subclass that is called before your Action classes get control, add a reauthentication. You will need to store the password in the Session object.

              In LoginAction add

              // Need the password for reauthentication in NotatbaseRequestProcessor
              session.setAttribute("password", j_password);


              The RequestProcessor subclass looks like this:

              public class NotatbaseRequestProcessor extends RequestProcessor{
               private static final Logger logger = Logger.getLogger(NotatbaseRequestProcessor.class);
              
               /**
               * Overriding RequestProcessor.processRoles to check permission for requested page
               * @param request The servlet request we are processing
               * @param response The servlet response we are creating
               * @param mapping The mapping we are using
               * @return Return true to continue normal processing, or false if returning to login page.
               * @throws IOException if an input/output error occurs
               * @throws ServletException if a servlet exception occurs
               */
               protected boolean processRoles(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping) throws IOException, ServletException {
               String contextPath = request.getContextPath();
               String requestURI = request.getRequestURI();
               String loginPage = "login.do";
               if (request != null) {
               Subject subject = (Subject)request.getSession().getAttribute("subject");
               if (subject != null)
               {
               // Get the Principal from Subject
               Set principals = subject.getPrincipals();
               Iterator it = principals.iterator();
               String principal = ((Principal)it.next()).getName();
              
               // Get the password from Session
               String password = (String)request.getSession().getAttribute("password");
              
               // Re authenticate the caller
               try
               {
               SecurityAssociationHandler handler = new SecurityAssociationHandler();
               SimplePrincipal user = new SimplePrincipal(principal);
               handler.setSecurityInfo(user, password);
               LoginContext loginContext = new LoginContext("notatbase", (CallbackHandler)handler);
              
               loginContext.login();
               logger.debug("User reauthenticated...");
               }
               catch (LoginException le)
               {
               logger.debug("Could not reauthenticate the user: "+le.getMessage());
               return false;
               }
               logger.debug("subject OK, returning true, " + subject);
               return true;
               } else if ( request.getRequestURI().equals("/notatbase/login.do")){
               logger.debug("login page, returning true");
               return true;
               }else {
               logger.debug("subject not OK, returning false");
               response.sendRedirect(contextPath + "/" + loginPage + "?requestedPage=" + removePrefix(requestURI, contextPath));
               return false;
               }
               }
               logger.debug("request is not OK, returning false");
               return false;
               }
              


              Yes this will authenticate the same user for each http request. A very crude workaround. But it does work

              Please give me any comments or clues you might have

              Regards,
              - Chris

              • 4. Re: Problems with JAAS authentication and Struts/EJB
                prilmeie

                I can confirm your troubles with JBoss 3.2.5 and Struts 1.2.4 - But I am not sure whose fault it is. The problem is very clear: JBoss forgets the user principal after the next http request.

                I haven't found any way to resolve that issue except reauthentification for each action. I have written myself an (AspectJ) aspect for constant reauthentification:

                public aspect WebAuthentificationAspect
                {
                 public pointcut authOperations ( HttpServletRequest request ) :
                 within ( de.prilmeier.mysabom.web.action.* ) &&
                 ! within ( de.prilmeier.mysabom.web.action.LoginAction ) &&
                 args ( *, *, request, * ) &&
                 execution ( * execute ( .., HttpServletRequest, .. ) );
                
                 before ( HttpServletRequest request ) throws Exception : authOperations ( request )
                 {
                 HttpSession session = request.getSession ( false );
                 String password = ( String ) session.getAttribute ( Constants.PASSWORD_KEY );
                 String userName = ( String ) session.getAttribute ( Constants.USER_NAME_KEY );
                
                 LoginCallbackHandler lch = new LoginCallbackHandler ( userName, password );
                 LoginContext lc = new LoginContext ( "mysabom", lch );
                 lc.login ();
                 }
                }


                That's no good programming style, but it works.

                • 5. Re: Problems with JAAS authentication and Struts/EJB
                  tschraepen

                  You guys should check out this article on using JAAS with Struts: http://www.mooreds.com/jaas.html

                  I think you'll find point 2.3.2 interesting.