Results 1 to 7 of 7

Thread: Unable to set BASIC authentication header

  1. #1

    Default Unable to set BASIC authentication header

    I have a rich client that accesses an Acegi-protected business logic module that is running in Tomcat.

    The remote calls work fine most of the time, but sometimes I get the following: (from the client's log)

    Code:
    2005-02-14 15:54:05,103 DEBUG
    [AuthenticationSimpleHttpInvokerRequestExecutor] Sending HTTP invoker
    request for service at [http://localhost:8081/Grace/Remote/DonorManager],
    with size 376
    2005-02-14 15:54:05,103 DEBUG
    [AuthenticationSimpleHttpInvokerRequestExecutor] Unable to set BASIC
    authentication header as ContextHolder: null; does not provide a
    SecureContext
    Which leads to a: net.sf.acegisecurity.AuthenticationCredentialsNotF oundException:
    Authentication credentials were not found in the SecureContext

    On normal method calls I get:

    Code:
    2005-02-14 15:53:57,728 DEBUG
    [AuthenticationSimpleHttpInvokerRequestExecutor] Sending HTTP invoker
    request for service at [http://localhost:8081/Grace/Remote/DepositManager],
    with size 389
    2005-02-14 15:53:57,728 DEBUG
    [AuthenticationSimpleHttpInvokerRequestExecutor] HttpInvocation now
    presenting via BASIC authentication ContextHolder-derived:
    net.sf.acegisecurity.providers.UsernamePasswordAuthenticationToken@1c6572b:
    Username: someuser; Password: [PROTECTED]; Authenticated: false; Details:
    null; Granted Authorities: ROLE_DATAENTRY
    The setup for the two business logic beans are exactly the same on both ends:

    Client:
    Code:
      <!-- Deposit Manager -->
      <bean id="depositManager" 
            class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean">
        <property name="serviceUrl"><value>$&#123;graceAdmin.moduleUrl&#125;/DepositManager</value></property>
        <property name="serviceInterface"><value>org.dm.daniel.grace.business.DepositManager</value></property>
        <property name="httpInvokerRequestExecutor"><ref local="httpInvokerRequestExecutor"/></property>
      </bean>
    
      <!-- Donor Manager -->
      <bean id="donorManager" 
            class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean">
        <property name="serviceUrl"><value>$&#123;graceAdmin.moduleUrl&#125;/DonorManager</value></property>
        <property name="serviceInterface"><value>org.dm.daniel.grace.business.DonorManager</value></property>
        <property name="httpInvokerRequestExecutor"><ref local="httpInvokerRequestExecutor"/></property>
      </bean>
    
      <bean id="httpInvokerRequestExecutor" class="net.sf.acegisecurity.ui.httpinvoker.AuthenticationSimpleHttpInvokerRequestExecutor"/>
    Server:
    Code:
      <bean id="depositManager" class="org.springframework.aop.framework.ProxyFactoryBean">
        <property name="proxyInterfaces"><value>org.dm.daniel.grace.business.DepositManager</value></property>
        <property name="interceptorNames">
          <list>
            <idref local="securityInterceptor"/>
            <idref local="depositManagerTarget"/>
          </list>
        </property>
      </bean>
    
      <bean id="donorManager" class="org.springframework.aop.framework.ProxyFactoryBean">
        <property name="proxyInterfaces"><value>org.dm.daniel.grace.business.DonorManager</value></property>
        <property name="interceptorNames">
          <list>
            <idref local="securityInterceptor"/>
            <idref local="donorManagerTarget"/>
          </list>
        </property>
      </bean>
    
    	<!-- 
    	    Security Interceptor	
    	-->
    	<bean id="securityInterceptor" class="net.sf.acegisecurity.intercept.method.aopalliance.MethodSecurityInterceptor">
    		<property name="validateConfigAttributes"><value>true</value></property>
    		<property name="authenticationManager"><ref local="authenticationManager"/></property>
    		<property name="accessDecisionManager"><ref local="accessDecisionManager"/></property>
    	    <!--    <property name="runAsManager"><ref local="runAsManager"/></property> -->
    		<property name="objectDefinitionSource">
    			<value>
    		        org.dm.daniel.grace.business.DepositManager.*=ROLE_DATAENTRY
    		        org.dm.daniel.grace.business.DonorManager.*=ROLE_DATAENTRY
    		        </value>
    		</property>
    	</bean>
    Why would the Context holder be available to populate the HTTP BASIC auth header on some method calls and not on others?

    Thanks ahead of time for your thoughts.

  2. #2
    Join Date
    Aug 2004
    Location
    Sydney, Australia
    Posts
    2,768

    Default

    It's definitely a client-side problem, and I suspect a threading one within your application. Your ContextHolder.getContext() is returning null, meaning the current thread of execution never had a ContextHolder.setContext(Context) take place. Fortunately Log4J can help you out by seeing the name of the thread performing calls. Use a pattern like this:

    Code:
    log4j.appender.stdout.layout.conversionPattern=&#91;%p,%c&#123;1&#125;,%t&#93; %m%n
    I suspect you will notice the problem matches the executing thread.

  3. #3

    Default

    Ben,
    Thanks for the swift reply. The method call that is problematic is in fact running in another thread. My client app is a standard GUI that is always spawning threads. How do I make it so that every thread in my program has the proper context holder static data set? It seems like I might just need to override
    net.sf.acegisecurity.ui.httpinvoker.Authentication SimpleHttpInvokerRequestExecutor
    to make it so that I get the context holder from some location that is available across threads. How would you handle this?

  4. #4
    Join Date
    Aug 2004
    Location
    Sydney, Australia
    Posts
    2,768

    Default

    Unfortunately I have no specific suggestions - it's really a client-specific issue and the approach will vary depending on your client threading design, availablility of a shared object holder etc.

  5. #5
    Join Date
    Nov 2004
    Posts
    3

    Default

    Quote Originally Posted by general_pattonm
    Ben,
    Thanks for the swift reply. The method call that is problematic is in fact running in another thread. My client app is a standard GUI that is always spawning threads. How do I make it so that every thread in my program has the proper context holder static data set? It seems like I might just need to override
    net.sf.acegisecurity.ui.httpinvoker.Authentication SimpleHttpInvokerRequestExecutor
    to make it so that I get the context holder from some location that is available across threads. How would you handle this?
    I have encountered the same issue. How did you end up resolving it?

  6. #6

    Default

    Instead of using the normal Acegi class for adding user's auth data to a remote request (AuthenticationSimpleHttpInvokerRequestExecutor), I created a new class based on it:

    Code:
    /**
     * Adds BASIC authentication support to <code>SimpleHttpInvokerRequestExecutor</code>,
     * but retrieves the current user's authentication data from Spring, not the
     * static ContextHolder, thus making it suitable for multi-threaded apps.
     * <P>
     * Based on <code>AuthenticationSimpleHttpInvokerRequestExecutor</code> by Ben Alex.
     * 
     * @author pattonm
     * @version $Revision&#58; 1.1 $
     */
    public class AuthCrossThreadHttpInvokerRequestExecutor extends SimpleHttpInvokerRequestExecutor &#123;
        /////////////////////////////
        // Normal member variables //
        /////////////////////////////
    
        protected CrossThreadContextHolder contextHolder = null;  
        
        /**
         * Provided so subclasses can perform additional configuration if required
         * &#40;eg set additional request headers for non-security related information
         * etc&#41;.
         *
         * @param con the HTTP connection to prepare
         * @param contentLength the length of the content to send
         *
         * @throws IOException if thrown by HttpURLConnection methods
         */
        protected void doPrepareConnection&#40;HttpURLConnection con, int contentLength&#41; throws IOException &#123;
        &#125;
    
        /**
         * Called every time a HTTP invocation is made.
         * 
         * <P>
         * Simply allows the parent to setup the connection, and then adds an
         * <code>Authorization</code> HTTP header property that will be used for
         * BASIC authentication.
         * </p>
         * 
         * <P>
         * The spring-managed <code>ContextHolder</code> is used to obtain the 
         * relevant principal and credentials.
         * </p>
         *
         * @param con the HTTP connection to prepare
         * @param contentLength the length of the content to send
         *
         * @throws IOException if thrown by HttpURLConnection methods
         * @throws AuthenticationCredentialsNotFoundException if the
         *         <code>ContextHolder</code> does not contain a valid
         *         <code>Authentication</code> with both its
         *         <code>principal</code> and <code>credentials</code> not
         *         <code>null</code>
         */
        protected void prepareConnection&#40;HttpURLConnection con, int contentLength&#41;
            throws IOException, AuthenticationCredentialsNotFoundException &#123;
            super.prepareConnection&#40;con, contentLength&#41;;
    
            // Add the basic authorization headers
            if &#40;contextHolder == null&#41; &#123;
                if &#40;logger.isDebugEnabled&#40;&#41;&#41; &#123;
                    logger.debug&#40;
                        "Unable to set BASIC authentication header as contextHolder " +
                        "member variable is null."&#41;;
                &#125;
            &#125;
            
            if &#40;&#40;contextHolder.getContext&#40;&#41; != null&#41;
                && &#40;contextHolder.getContext&#40;&#41; instanceof SecureContext&#41;&#41; &#123;
                Authentication auth = &#40;&#40;SecureContext&#41; contextHolder.getContext&#40;&#41;&#41;
                    .getAuthentication&#40;&#41;;
    
                if &#40;&#40;auth != null&#41; && &#40;auth.getPrincipal&#40;&#41; != null&#41;
                    && &#40;auth.getCredentials&#40;&#41; != null&#41;&#41; &#123;
                    String base64 = auth.getPrincipal&#40;&#41;.toString&#40;&#41; + "&#58;"
                        + auth.getCredentials&#40;&#41;.toString&#40;&#41;;
                    con.setRequestProperty&#40;"Authorization",
                        "Basic "
                        + new String&#40;Base64.encodeBase64&#40;base64.getBytes&#40;&#41;&#41;&#41;&#41;;
    
                    if &#40;logger.isDebugEnabled&#40;&#41;&#41; &#123;
                        logger.debug&#40;
                            "HttpInvocation now presenting via BASIC authentication ContextHolder-derived&#58; "
                            + auth.toString&#40;&#41;&#41;;
                    &#125;
                &#125; 
                else &#123;
                    if &#40;logger.isDebugEnabled&#40;&#41;&#41; &#123;
                        logger.debug&#40;
                            "Unable to set BASIC authentication header as ContextHolder&#58; "
                            + contextHolder.getContext&#40;&#41;
                            + "; did not provide valid Authentication&#58; " + auth&#41;;
                    &#125;
                &#125;
            &#125; 
            else &#123;
                if &#40;logger.isDebugEnabled&#40;&#41;&#41; &#123;
                    logger.debug&#40;
                        "Unable to set BASIC authentication header as ContextHolder&#58; "
                        + contextHolder.getContext&#40;&#41;
                        + "; does not provide a SecureContext"&#41;;
                &#125;
            &#125;
    
            doPrepareConnection&#40;con, contentLength&#41;;
        &#125;
        
        public CrossThreadContextHolder getContextHolder&#40;&#41; &#123;
            return contextHolder;
        &#125;
        public void setContextHolder&#40;CrossThreadContextHolder contextHolder&#41; &#123;
            this.contextHolder = contextHolder;
        &#125;
    &#125;
    It uses a CrossThreadContextHolder, which, instead of storing the data in a static member variable, uses a normal non-static member variable:

    Code:
    /**
     * Associates a given Context with a member variable, for use across 
     * multiple execution threads.
     * 
     * @author pattonm
     * @version $Revision&#58; 1.2 $
     */
    public class CrossThreadContextHolder &#123;
        private Context context = null;
        
        public CrossThreadContextHolder&#40;&#41; &#123;
        &#125;
        
        public Context getContext&#40;&#41; &#123;
            return context;
        &#125;
        public void setContext&#40;Context context&#41; &#123;
            this.context = context;
        &#125;
    &#125;
    Then, I just put both of them in Spring:

    Code:
      <!-- Automatically propagates ContextHolder-managed Authentication principal
           and credentials to a HTTP invoker BASIC authentication header -->
      <bean id="httpInvokerRequestExecutor" class="org.dm.daniel.grace.ui.util.AuthCrossThreadHttpInvokerRequestExecutor">
        <property name="contextHolder"><ref bean="contextHolder"/></property>
      </bean>
    
      <!-- Bean for holding the secure context which holds the currently logged in
    	   user's authentication principal and credentials -->
      <bean id="contextHolder" class="org.dm.daniel.grace.ui.util.CrossThreadContextHolder"/>
    And after I've authenticated I put the authentication class in the CrossThreadContextHolder:

    Code:
    	    	CrossThreadContextHolder contextHolder = &#40;CrossThreadContextHolder&#41;appCtx.getBean&#40;"contextHolder"&#41;;
    
    	    	SecureContext secureContext = new SecureContextImpl&#40;&#41;;
    	        secureContext.setAuthentication&#40;result&#41;;
    	       	contextHolder.setContext&#40;secureContext&#41;;
    I don't know Acegi too well so their might be problems to this admittedly naive approach, but it works fine for me since both threads can access spring, but both can't access the static data.

  7. #7
    Join Date
    Aug 2004
    Location
    Sydney, Australia
    Posts
    2,768

    Default

    Your approach looks fine to me. You're setting the BASIC authentication headers on your client, and that's all that really needs to be done from an Acegi Security perspective.

Similar Threads

  1. Replies: 4
    Last Post: May 15th, 2006, 09:38 AM
  2. Beandoc crashing (on its samples!)
    By aaime in forum Container
    Replies: 17
    Last Post: Oct 7th, 2005, 07:21 AM
  3. Loosing my SecureContext
    By sklakken in forum Security
    Replies: 3
    Last Post: Jul 21st, 2005, 01:44 PM
  4. Replies: 8
    Last Post: Apr 3rd, 2005, 05:55 PM
  5. Replies: 8
    Last Post: Dec 7th, 2004, 06:13 PM

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •