Announcement Announcement Module
Collapse
No announcement yet.
JaxRpc client side authentication. Page Title Module
Move Remove Collapse
This topic is closed
X
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • JaxRpc client side authentication.

    I haven't seen any posts regarding client side authentication for JaxRpc services so I thought i'd share my experience.

    Our application consists of a rich client that communicates with a server. All the server side services are exposed through axis and required basic authenticaion.

    I implemented the client side authentication using the remote authentication manager as explained here http://forum.springframework.org/viewtopic.php?t=2842. This method works wonderfully, but then i ran into the issue of how to ensure that the JaxRpc calls are populated with the correct credentials.

    Since acegi provides the AuthenticationSimpleHttpInvokerRequestExecuter, but not a JaxRpc version, i simply extended spring's JaxRpcPortProxyFactoryBean and overrode the following methods to populate the security credentials.

    Code:
      protected void preparePortStub (Stub _stub)
      {
        super.preparePortStub(_stub);
    
        Authentication auth = getAuthentication();
        if(auth != null){
          _stub._setProperty(Stub.USERNAME_PROPERTY,
              auth.getPrincipal().toString());
          _stub._setProperty(Stub.PASSWORD_PROPERTY,
              auth.getCredentials().toString());
        }
      }
    
      protected void prepareJaxRpcCall (Call _call)
      {
        super.prepareJaxRpcCall(_call);
    
        Authentication auth = getAuthentication();
        if(auth != null){
          _call.setProperty(Call.USERNAME_PROPERTY, auth.getPrincipal().toString());
          _call.setProperty(Call.PASSWORD_PROPERTY,
              auth.getCredentials().toString());
        }
      }
    
      protected Authentication getAuthentication ()
      {
        // acegi 0.9.x
        //Authentication auth = SecurityContext.getAuthentication();
        // acegi 0.8.x
        SecureContext context = (SecureContext)ContextHolder.getContext();
        Authentication auth = context.getAuthentication();
    
        return auth;
      }
    Now once the client side has been authenticated and the security context populated, all jaxrpc calls will include the proper authentication properties.

    You can of course take this a step further and modify getAuthentication() to provide an anonymous Authentication or some other variant.

    Hopefully some of you will find this useful.

  • #2
    Hi Eric

    Thanks for sharing. Do you have any unit tests? Would you mind if we included this in the core CVS?

    Comment


    • #3
      no unit tests as of yet for the extension. I just have integration tests for the services i'm exposing.

      If you want i can implement a unit test for it using your AuthenticationSimpleHttpInvokerRequestExecutorTest s as a template to ensure i stick with your coding conventions.

      Then i can submit both back to you for inclusion in the core. Or if you prefer you can just grab the code here and run with that.

      Comment


      • #4
        For those of you planning to use this method of securing web services, you may run into a little snag. What to do about the wsdls?

        I recieved one PM asking this question and figured that others could benefit from this info as well.

        Axis has the convient method of generating wsdls on the fly by appending "?wsdl" to the service url. Most people will probably take advantage of that and use that url as the wsdlDocumentUrl property for your service. Well, if you so happened to have secured your services with a url pattern like "/service/*" then all your wsdls will be secured as well.

        By default when spring initializes your services and trys to hit the wsdl url, it does not provide any authentication info (not even if you supply values to the username/password properties). So if you secured the wsdl url, then spring will fail with a 401 http error code.

        There are 2 ways you can solve this issue.
        1) allow anonymous access to the wsdls.
        2) modify the axis service to provide authentication to the wsdl parser.

        The first method is the easiest, but obviously a possible security risk depending on the sensitivity of your wsdls.

        To allow anonymous access you can make use of acegi's anonymous process filter.
        Simply add the anonymous process filter to either your web.xml or filterChainProxy configuration (depending on the route you took with your other filters). Then configure your filter invocation interceptor with a new url pattern for your wsdls.

        Code:
          <bean id="filterInvocationInterceptor"
              parent="abstractFilterInvocationInterceptor"
              class="net.sf.acegisecurity.intercept.web.FilterSecurityInterceptor">
            <property name="objectDefinitionSource">
              <value>
                CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
                \A/services/.*\?wsdl\Z=ROLE_ANONYMOUS
                \A/services/.*\Z=GROUP_USER,GROUP_ADMINISTRATOR
              </value>
            </property>
          </bean>

        This should allow anyone to now access all your wsdls.


        The second method is a bit trickier.
        What you will need to do is patch your axis jar by modifying org.apache.axis.client.Service (i'm assuming you are using the 1.2 final release for this example).

        The reason you have to patch the jar instead of just extending the class is that the axis code is not extension friendly. The Service class has several core methods marked private (one that you need to modify) and they don't encapsulate their member data with getters and setters. If they simply made their initService() methods protected then you could simply implement a new ServiceFactory and set the serviceFactoryClass propery of your JaxRpcPortFactoryBean, but alas, they didn't make our lives easy in this respect.

        So, what you need to do is modify the initService(String,QName) method of org.apache.axis.client.Service.

        Code:
            private void initService&#40;String url, QName serviceName&#41;
                    throws ServiceException &#123;
                try &#123;
                    // Start by reading in the WSDL using Parser
                    Parser parser = new Parser&#40;&#41;;
        // CHANGE
                    // acegi 0.9.x
                    //Authentication auth = SecurityContext.getAuthentication&#40;&#41;;
                    // acegi 0.8.x
                    SecureContext context = &#40;SecureContext&#41;ContextHolder.getContext&#40;&#41;;
                    Authentication auth = context != null ?
                      context.getAuthentication&#40;&#41; &#58; null;
        
                    if&#40;auth != null&#41;&#123;
                      parser.setUsername&#40;auth.getPrincipal&#40;&#41;.toString&#40;&#41;&#41;;
                      parser.setPassword&#40;auth.getCredentials&#40;&#41;.toString&#40;&#41;&#41;;
                    &#125;
        // END CHANGE
                    parser.run&#40;url&#41;;
        
                    if &#40;cachingWSDL && this.wsdlLocation != null&#41;
                        cachedWSDL.put&#40;url, parser&#41;;
        
                    initService&#40;parser, serviceName&#41;;
                &#125; catch &#40;Exception exp&#41; &#123;
                    throw new ServiceException&#40;
                            Messages.getMessage&#40;"wsdlError00", "" + "", "\n" + exp&#41;,
                            exp&#41;;
                &#125;
            &#125;
        You'll notice that the changes i made are marked with a //CHANGE block. You will also need to add the appropriate import statements for the new acegi classes being referenced.


        Once you have patched the axis code, you will then need to set lazy-init="true" on all your services. This will prevent spring from trying to intialize them before the proper authentication has taken place (authentication that will be necessary to allow access to the necessary wsdls).


        If anyone has any questions, i'd be happy to try to answer them. Or if someone comes up with a more elegent alternative to pathing the axis code, i would love to hear it. I really hate patching code in this manner, but i currently don't see any way around it, short of re-implementing the Service class.

        Comment

        Working...
        X