Announcement Announcement Module
Collapse
No announcement yet.
HessianProtocolException with incorrect login Page Title Module
Move Remove Collapse
This topic is closed
X
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • HessianProtocolException with incorrect login

    Hello,
    I am new to Acegi and am trying to get it working for my application, which is a SWT thick client talking to Business Methods in Tomcat (via Hessian).
    I have it mostly working but I have the following hangup: when I try to authenticate a user with bad credentials (ie, bad password), I get a HessianProtocolException:
    org.springframework.remoting.RemoteAccessException : Cannot access Hessian service at [http://localhost:8081/GraceWeb/Remote/SampleManager]; nested exception is com.caucho.hessian.io.HessianProtocolException: etc...

    This is not what I would expect. I would expect a BadCredentialsException, I guess.

    When I have a user who doesn't have enough privileges to call a certain method (but has a correct u/p), I get a lovely:
    net.sf.acegisecurity.AccessDeniedException: Access is denied.
    as I would expect.

    Why am I getting the HessianProtocolException instead of the BadCredentialsException in the first scenario? I can provide config info if necessary.

    Btw: excellent work, Ben! This is so much better than trying to get security working with JBoss (where I am migrating from). I never could get it to work because the docs were so sparse and there were no examples. But Acegi is much more usable.

    Thanks,
    Matt

  • #2
    Acegi Security uses BASIC authentication with the remoting protocols. It implements RFC 1945 (http://www.faqs.org/rfcs/rfc1945.html), section 11.1. As such it can only return HTTP headers consistent with "authentication failed", as per the RFC. At an implementation level Acegi Security re-launches the BasicProcessingFilterEntryPoint, which sends back a 401 response with a "WWW-Authenticate" header.

    Remoting protocols will handle 401s in different ways. Typically they'll fail in a non-graceful manner, such as you're seeing with Hessian.

    The preferred approach is to use the net.sf.acegisecurity.providers.rcp package BEFORE calling any Hessian services. This package provides a way to ensure the username and password is correct. You can then safely use that validate username and password with the Hessian beans, knowing they should execute.

    Comment


    • #3
      Thanks for the reply. I've checked the RCP out of CVS and have adapted the code that uses the net.sf.acegisecurity.providers.rcp package. However, I am hitting a wall that I think is due to a problem in my bean configuration. When I call authenticationManager.authenticate() on my client, I get an InvocationTargetException that is caused by:

      Caused by: java.lang.IllegalArgumentException: Cannot use default constructor
      at net.sf.acegisecurity.GrantedAuthorityImpl.<init>(G rantedAuthorityImpl.java:45)
      ... 25 more

      Unfortunately, I haven't figured out how to catch this exception to get the 25 more. Any idea what the problem could be?

      The following is my bean configuration:

      ---------------------------
      <!-- Hessian exporter for the RemoteAuthenticationManager -->
      <bean name="/RemoteAuthenticationManager" class="org.springframework.remoting.caucho.Hessian ServiceExporter">
      <property name="service"><ref bean="remoteAuthenticationManager"/></property>
      <property name="serviceInterface"><value>net.sf.acegisecurit y.providers.rcp.RemoteAuthenticationManager</value></property>
      </bean>


      <bean id="remoteAuthenticationManager" class="net.sf.acegisecurity.providers.rcp.RemoteAu thenticationManagerImpl">
      <property name="authenticationManager"><ref bean="authenticationManager"/></property>
      </bean>

      <bean id="authenticationManager" class="net.sf.acegisecurity.providers.ProviderMana ger">
      <property name="providers">
      <list>
      <ref local="daoAuthenticationProvider"/>
      </list>
      </property>
      </bean>

      <bean id="daoAuthenticationProvider" class="net.sf.acegisecurity.providers.dao.DaoAuthe nticationProvider">
      <property name="authenticationDao"><ref local="inMemoryDaoImpl"/></property>
      </bean>

      <bean id="inMemoryDaoImpl" class="net.sf.acegisecurity.providers.dao.memory.I nMemoryDaoImpl">
      <property name="userMap">
      <value>
      marissa=koala,ROLE_TELLER,ROLE_SUPERVISOR
      dianne=emu,ROLE_TELLER
      scott=wombat,ROLE_TELLER
      peter=opal,disabled,ROLE_TELLER
      </value>
      </property>
      </bean>


      <bean id="accessDecisionManager" class="net.sf.acegisecurity.vote.AffirmativeBased" >
      <property name="allowIfAllAbstainDecisions"><value>false</value></property>
      <property name="decisionVoters">
      <list>
      <ref local="roleVoter"/>
      </list>
      </property>
      </bean>

      <bean id="roleVoter" class="net.sf.acegisecurity.vote.RoleVoter"/>


      <bean id="basicProcessingFilter" class="net.sf.acegisecurity.ui.basicauth.BasicProc essingFilter">
      <property name="authenticationManager"><ref local="authenticationManager"/></property>
      <property name="authenticationEntryPoint"><ref local="basicProcessingFilterEntryPoint"/></property>
      </bean>

      <bean id="basicProcessingFilterEntryPoint" class="net.sf.acegisecurity.ui.basicauth.BasicProc essingFilterEntryPoint">
      <property name="realmName"><value>Contacts Realm</value></property>
      </bean>

      <bean id="autoIntegrationFilter" class="net.sf.acegisecurity.ui.AutoIntegrationFilt er" />

      <!-- End Acegi Security Configuration -->

      Comment


      • #4
        Is your exception being thrown on the client or on the server?

        Importantly, if you use a different remoting protocol - such as HttpInvoker - does it still happen? It might be serialization related, although as far as I can recall the package you're using was tested with Hessian.

        Comment


        • #5
          Ben,
          Thanks again for your quick reply. The exception I mentioned before is being thrown on the server. I tried switching to HttpInvoker, but I get the following:

          net.sf.acegisecurity.AuthenticationCredentialsNotF oundException: A valid SecureContext was not provided in the RequestContext

          exception: A valid SecureContext was not provided in the RequestContext

          at net.sf.acegisecurity.intercept.AbstractSecurityInt erceptor.interceptor(AbstractSecurityInterceptor.j ava:280)
          at net.sf.acegisecurity.intercept.method.MethodSecuri tyInterceptor.invoke(MethodSecurityInterceptor.jav a:82)
          at org.springframework.aop.framework.ReflectiveMethod Invocation.proceed(ReflectiveMethodInvocation.java :144)
          at org.springframework.aop.framework.JdkDynamicAopPro xy.invoke(JdkDynamicAopProxy.java:174)
          (etc)

          You say this is usually caused by the AutoIntegrationFilter not being in place, but I checked the logs and it is. In fact, I was getting this before with Hessian, and it was adding the AutoIntegrationFilter that fixed it.

          Now I am at an impasse with both protocols: Hessian and HttpInvoker. Can you help me fix either problem?

          Comment


          • #6
            Let's focus on getting HttpInvoker to work first, as I know it actually works reliably (whilst with Hessian I've had weird serialization issues).

            If AutoIntegrationFilter (which BTW disappears in release 0.7.0 so maybe switch to HttpSessionIntegrationFilter) is in place, changes are your server is not picking up the BASIC authentication headers. However, I can see BasicProcessingFilter defined in your application context, so I'm wondering if it has a corresponding filter definition in web.xml and the associated filter mapping executes before the AutoIntegrationFilter filter mapping.

            Would you please post web.xml and confirm the previously posted applicationContext.xml has not changed.

            Also, are you actually setting the BASIC authentication headers on the HttpInvokter client proxy? In Acegi Security CVS there is a nice extension called net.sf.acegisecurity.httpinvoker.AuthenticationSim pleHttpInvoker which automatically sets the necessary header on the client proxy, deriving the username and password to use from the client-side ContextHolder. Perhaps try the latest CVS. There are build instructions at http://acegisecurity.sourceforge.net.

            Comment


            • #7
              Ben,
              Thanks again for your help. I switched my filters to the right order, but still no success. I also am unsure as to whether I am setting the BASIC authentication headers on the HttpInvokter client proxy. With Hessian, I would just do:

              Code:
              		
              Method method = mgr.getClass&#40;&#41;.getMethod&#40;"setUsername", new Class&#91;&#93; &#123; String.class &#125;&#41;;
              method.invoke&#40;mgr, new Object&#91;&#93; &#123; getUsername&#40;&#41; &#125;&#41;;
              method = mgr.getClass&#40;&#41;.getMethod&#40;"setPassword", new Class&#91;&#93; &#123; String.class &#125;&#41;;
              method.invoke&#40;mgr, new Object&#91;&#93; &#123; getPassword&#40;&#41; &#125;&#41;;
              However, org.springframework.remoting.httpinvoker.HttpInvok erProxyFactoryBean has no such methods.

              So currently, I am just doing:

              Code:
              AuthenticationManager authenticationManager = &#40;AuthenticationManager&#41;appCtx.getBean&#40;"authenticationManager"&#41;;
              
              UsernamePasswordAuthenticationToken request = new UsernamePasswordAuthenticationToken&#40;getUsername&#40;&#41;,
                                  getPassword&#40;&#41;&#41;;
              
              Authentication result = authenticationManager.authenticate&#40;request&#41;;
              It is at the last line above (the authenticate call) that I am crashing with the exception: A valid SecureContext was not provided in the RequestContext

              The following is from my client applicationContext.xml:

              Code:
              <bean id="authenticationManager" class="net.sf.acegisecurity.providers.ProviderManager">
                <property name="providers">
                  <list>
                    <ref bean="remoteAuthenticationProvider"/>
                  </list>
                </property>
              </bean>
              
              <bean id="remoteAuthenticationProvider" class="net.sf.acegisecurity.providers.rcp.RemoteAuthenticationProvider">
                <property name="remoteAuthenticationManager"><ref bean="remoteAuthenticationManager"/></property>
              </bean>
              
              <bean id="remoteAuthenticationManager" class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean">
                <property name="serviceInterface"><value>net.sf.acegisecurity.providers.rcp.RemoteAuthenticationManager</value></property>
                <property name="serviceUrl"><value>$&#123;graceAdmin.serviceUrl&#125;/Grace/Remote/RemoteAuthenticationManager</value></property>
              </bean>
              
              <!-- Proxy for my business logic object -->
              <bean id="sampleManager" class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean">
                <property name="serviceUrl"><value>$&#123;graceAdmin.serviceUrl&#125;/Grace/Remote/SampleManager</value></property>
                <property name="serviceInterface"><value>org.dm.daniel.grace.business.SampleManager</value></property>
              </bean>
              The following is my web.xml (I tried the HttpSessionIntegrationFilter class to no avail, so it is back to the AutoIntegrationFilter):
              Code:
              <web-app>
                <display-name>Grace Web</display-name>
                <description>Grace Web Services &#40;currently only exposes remote methods&#41;.</description>
              
                <!-- Filters for Acegi Security -->
              
                <filter>
                  <filter-name>Acegi HTTP BASIC Authorization Filter</filter-name>
                  <filter-class>net.sf.acegisecurity.util.FilterToBeanProxy</filter-class>        
                  <init-param>
                    <param-name>targetClass</param-name>
                    <param-value>net.sf.acegisecurity.ui.basicauth.BasicProcessingFilter</param-value>
                  </init-param>
                </filter>
              
                <filter>
                  <filter-name>Acegi Security System for Spring Auto Integration Filter</filter-name>
                  <filter-class>net.sf.acegisecurity.util.FilterToBeanProxy</filter-class>
                  <init-param>
                    <param-name>targetClass</param-name>
              <param-value>net.sf.acegisecurity.ui.AutoIntegrationFilter</param-value>
                  </init-param>
                 </filter>
              
                <filter-mapping>
                  <filter-name>Acegi HTTP BASIC Authorization Filter</filter-name>
                  <url-pattern>/*</url-pattern>
                </filter-mapping>
              
                <filter-mapping>
                  <filter-name>Acegi Security System for Spring Auto Integration Filter</filter-name>
                  <url-pattern>/*</url-pattern>
                </filter-mapping>
              
                <!-- End of Filters for Acegi Security -->
              
                <!-- Listener to initialize the spring application context -->
                <listener>
                  <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
                </listener>
              
                <!--
                  Servlet that dispatches request to registered handlers &#40;Controller 
                  implementations&#41;.
                  
                  Has its own application context, by default defined in 
                  "&#123;servlet-name&#125;-servlet.xml", i.e. "remote-servlet.xml".
                -->
                <servlet>
              	<servlet-name>remote</servlet-name>
              	<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
              	<load-on-startup>2</load-on-startup>
                </servlet>
              
                <!-- All remote method calls use the URL path /Remote -->
                <servlet-mapping>
                  <url-pattern>/Remote/*</url-pattern>
                  <servlet-name>remote</servlet-name>
                </servlet-mapping>
              
              </web-app>
              The following is from my remote-servlet.xml:
              Code:
                <bean name="/SampleManager" class="org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter">
                  <property name="service"><ref bean="sampleManager"/></property>
                  <property name="serviceInterface">
                      <value>org.dm.daniel.grace.business.SampleManager</value>
                  </property>
                </bean>
              
                <bean name="/RemoteAuthenticationManager" class="org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter">
                  <property name="service"><ref bean="remoteAuthenticationManager"/></property>
                  <property name="serviceInterface"><value>net.sf.acegisecurity.providers.rcp.RemoteAuthenticationManager</value></property>
                </bean>
              My server's applicationContext.xml has stayed the same as above, with the exception of the HessianExporter.
              I haven't yet gone to your CVS to get the net.sf.acegisecurity.httpinvoker.AuthenticationSim pleHttpInvoker since it seems we should be able to do this with the current version.

              Thanks again for all of your help, Ben.

              Comment


              • #8
                An interesting issue...

                Just let me confirm, on the server side you are getting an AuthenticationCredentialsNotFoundException, which includes in its stack trace net.sf.acegisecurity.intercept.method.MethodSecuri tyInterceptor.invoke(MethodSecurityInterceptor.jav a:82), when you execute the code block you just gave which includes authenticationManager.authenticate(request)?

                The only problem I see with your configuration is your client-side sampleManager bean will need an extra property and associated request executor. It should look something like this:

                Code:
                <!-- Proxy for my business logic object -->
                <bean id="sampleManager" class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean">
                  <property name="serviceUrl"><value>$&#123;graceAdmin.serviceUrl&#125;/Grace/Remote/SampleManager</value></property>
                  <property name="serviceInterface"><value>org.dm.daniel.grace.business.SampleManager</value></property>
                </bean>
                  <property name="httpInvokerRequestExecutor">
                    <ref local="httpInvokerRequestExecutor"/>
                  </property>
                
                <!-- Automatically propagates ContextHolder-managed Authentication principal
                		 and credentials to a HTTP invoker BASIC authentication header -->
                <bean id="httpInvokerRequestExecutor" class="net.sf.acegisecurity.ui.httpinvoker.AuthenticationSimpleHttpInvokerRequestExecutor"/>
                I'm sure that comment is helpful, but just to repeat it, you need to set the username and password to use into the client-side ContextHolder. However, before you do that you need to validate the username and password are correct and this is the purpose of your remoteAuthenticationManager bean. It does NOT need the httpInvokerRequestExecutor property, as it is designed to not set any BASIC authentication headers.

                I have no idea why on the server-side you've got MethodSecurityInterceptor intercepting the call to your net.sf.acegisecurity.providers.rcp.RemoteAuthentic ationManager. It simply shouldn't be. I cannot see any declarations in your earlier posted application context for MethodSecurityInterceptor, so I'm not sure why it's firing. Is there some extra application context XML that has not yet been posted? It might also help to switch on debug level logging, showing the server-side output leading up MethodSecurityInterceptor causing the AuthenticationCredentialsNotFoundException.

                MethodSecurityInterceptor should never be being called for RemoteAuthenticationManager. A request via your client-side remoteAuthenticationManager should NOT have any BASIC authentication headers. Here's the RemoteAuthenticationManagerImpl, and as you can see, it doesn't need the ContextHolder to be setup:

                Code:
                    public GrantedAuthority&#91;&#93; attemptAuthentication&#40;String username,
                        String password&#41; throws RemoteAuthenticationException &#123;
                        UsernamePasswordAuthenticationToken request = new UsernamePasswordAuthenticationToken&#40;username,
                                password&#41;;
                
                        try &#123;
                            return authenticationManager.authenticate&#40;request&#41;.getAuthorities&#40;&#41;;
                        &#125; catch &#40;AuthenticationException authEx&#41; &#123;
                            throw new RemoteAuthenticationException&#40;authEx.getMessage&#40;&#41;&#41;;
                        &#125;
                    &#125;
                I also note the JavaDocs for RemoteAuthenticationProvider:

                Code:
                 * The <code>RemoteAuthenticationManager</code> should not require any special
                 * username or password setting on the remoting client proxy factory to
                 * execute the call. Instead the entire authentication request must be
                 * encapsulated solely within the <code>Authentication</code> request object.
                 * In practical terms this means the <code>RemoteAuthenticationManager</code>
                 * will <B>not</B> be protected by BASIC or any other HTTP-level
                 * authentication.
                I look forward to the debug logging (and any extra application context XML)!

                Comment


                • #9
                  I'm a n00b...thanks to your surprise about the MethodSecurityInterceptor, I realized that the crash is taking place NOT with the RemoteAuthenticationManager, but rather with the SampleManager (the business logic class). So I tried putting the SecureContext together with the following code:

                  Code:
                              // Setup a secure ContextHolder &#40;if required&#41;
                              if &#40;ContextHolder.getContext&#40;&#41; == null || !&#40;ContextHolder.getContext&#40;&#41; instanceof SecureContext&#41;&#41; &#123;
                                  try &#123;
                                      ContextHolder.setContext&#40;&#40;SecureContext&#41;getSecureContextClass&#40;&#41;.newInstance&#40;&#41;&#41;;
                                  &#125;
                                  catch &#40;Exception e&#41; &#123;
                                      throw new RuntimeException&#40;e&#41;;
                                  &#125;
                              &#125;
                  
                              // Commit the successful Authentication object to the secure
                              // ContextHolder
                              SecureContext sc = &#40;SecureContext&#41;ContextHolder.getContext&#40;&#41;;
                              sc.setAuthentication&#40;result&#41;;
                                 // result here is the return value from authenticate&#40;&#41;
                              ContextHolder.setContext&#40;sc&#41;;
                      		
                     // Call the business method    	
                    SampleManager sampleMgr = &#40;SampleManager&#41;appCtx.getBean&#40;"sampleManager"&#41;;
                    sampleMgr.setGreeting&#40;"hi there"&#41;;
                  And it is at setGreeting() that the crash is occurring.

                  However, I still am getting the same error. Is there a way I need to associate the context holder with the SampleManager factory?
                  It seems that the "httpInvokerRequestExecutor" is what is needed for this, and I tried adding it as you specified, but I cannot find the definition for this class in the acegi jars. Where is it?

                  I think I'm getting close!

                  Comment


                  • #10
                    Yes, AuthenticationSimpleHttpInvokerRequestExecutor is the critical class. What it does is look at the ContextHolder, get the Authentication from it, and set BASIC authentication headers on your HttpInvoker request so they get presented to the server. On the server side, it's very easy for it to use these BASIC authentication headers and create a new Authentication on the server-side ContextHolder.

                    You will find net.sf.acegisecurity.ui.httpinvoker.Authentication SimpleHttpInvokerRequestExecutor in CVS HEAD. It doesn't exist in official release 0.6.1. There are detailed (yet simple to follow) build instructions at http://acegisecurity.sourceforge.net/building.html

                    Comment


                    • #11
                      Thanks, Ben. After downloading the 0.7.0 code from CVS and compiling it, I was able to get it to work with HttpInvoker! Thanks for all of your help throughout.

                      Comment

                      Working...
                      X