Spring Remoting with Security and SSL

One of my favorite features of the Spring Framework is the Spring Remoting part, which enables you to expose any bean in a Spring Application Context as a remote service over HTTP. It’s fast, it’s easy, and it’s really, really simple.

Basic Spring Remoting Configuration

In the general situation all you need to do is create a DispatcherServlet (just as you would with any Spring MVC application), add an Exporter on the server side and reference a ProxyFactoryBean on the client.
On the server side:

web.xml

  contextConfigLocation
  /WEB-INF/applicationContext.xml



  org.springframework.web.context.ContextLoaderListener


  demo
  org.springframework.web.servlet.DispatcherServlet
    
      contextConfigLocation
      /WEB-INF/demo-servlet.xml
    
    1



  demo
  /*

demo-servlet.xml - exposes bean 'helloService' as remote service

  
  

On the client side:

clientContext.xml

  
  

Now, in the client application all you need to do is ask for the ‘helloService‘ bean and you will be handed a proxy that talks to the target service on the server without the server or the client knowing anything about it.

Securing the Remote Service

Now, in many cases you’ll want to apply some security restrictions on the exposed HTTP service. Being in the Spring world the natural choice for this purpose will be Spring Security. Far from the complications of its predecessor Acegi, Spring Security configuration is now a matter of very few lines of XML code:

web.xml
...

  springSecurityFilterChain
  org.springframework.web.filter.DelegatingFilterProxy



  springSecurityFilterChain
  /*

...
demo-servlet.xml - additions to the original file above; default with one hard coded user

...

  
  


  
    
  

Note that we’re defining the Spring Security XML schema in the schema definition.

clientContext.xml

  
  
  
    
public class BasicAuthenticationCommonsHttpInvokerRequestExecutor extends
  CommonsHttpInvokerRequestExecutor {

  @Override
  protected PostMethod createPostMethod(HttpInvokerClientConfiguration config) throws IOException {
    PostMethod postMethod = super.createPostMethod(config);

    Authentication auth =
        SecurityContextHolder.getContext().getAuthentication();

    if ((auth != null) && (auth.getName() != null) &&
          (auth.getCredentials() != null)) {
      String base64 = auth.getName() + ":" + auth.getCredentials().toString();
      postMethod.setRequestHeader("Authorization", "Basic " +
          new String(Base64.encodeBase64(base64.getBytes())));
    }

    return postMethod;
  }
}

Now all you need to do is specify this implementation as HttpInvokerRequestExecutor for your client ProxyFactoryBean and you’re all set:

clientContext.xml

  
  
  
    
  

This Post Has 20 Comments

  1. Gerardo

    and what about ssl whit client authentication (using digitals certificates in both sides, the client and the server.)

    1. Mattias Hellborg Arthursson

      Never had any reason to look into that. I would expect however that you could use a similar approach as the one described in this post. The HttpClient documentation suggests you’ll have to do some custom tweaks to make it work, but at least there seems to be places for you to hook in for doing custom authentication.

  2. shah

    I really appreciate your entry. I’m working on something that may be related, but I’am stuck somewhere. I’m trying to use spring remoting with spring security, but I’m not running a web app. I am trying to authenticate a user against an LDAP and then save his credentials in the spring security context. I removed the filter and filter mapping as it was trying to authenticate my request. However I created an authenticate class that authenticates and then I place the authentication object in the security context. However, the next time I call the function remotly, I don’t see the authentication object anymore. Have you tried to manipulate the security context manually like this? I have also tried SecurityContextHolder
    .setStrategyName(SecurityContextHolder.MODE_GLOBAL);

    Thanks in advance,

    1. Mattias Hellborg Arthursson

      The problem in this case is that there’s no concept of sessions; using Spring Remoting everything is typically stateless. This means that each request is ‘new’ to the server and that you’ll either have to authenticate each and every call (which is what happens with the built-in filters and basic authentication – the principal and credentials will be sent with each and every request), or you’ll have to introduce some kind of custom session token which you send back and forth between the server and client.

      I don’t think I understand your requirements completely; why can you not use the built-in filters and Spring Security out of the box to perform authentication? It’s perfectly manageable to use Spring Security to perform LDAP authentication.

  3. shah

    Thanks for the quick reply,
    Our architects don’t like the idea of authenticating each request due to the lag of hitting a remote LDAP server each and every time. I may have to go with the custom approach of passing back some kind of session id or something.

  4. shah

    Hello,
    I’ve actually been struggling with the for a while. Maybe you can point me in the right direction as there really isn’t any documentation except the spring docs which are brief and forums.
    I’ve already setup the LDAP authentication, but it happens manualy on the server side. If I want to use the out of the box implementation I think I need to do the following:

    1.) Add the credentials on the client side and then pass them to the server. I think I want to use CommonsHttpInvokerRequestExecutor, but it seems to only hold information for BASIC authentication. How do I send this via the request. In your example, you also utilize Authentication auth =
    SecurityContextHolder.getContext().getAuthentication();
    Do I manually create a local security context and set authentication information into it?

    2.) I assume once I send this, I can use the normal filters to authenticate.

    Thanks for the help.

  5. Mattias Hellborg Arthursson

    Unless you have very particular requirements you should avoid doing this manually and use the out-of-the-box implementation instead. Using CommonsHttpInvokerRequestExecutor the appropriate headers will automatically be added. The filters on the server side will extract that information and use it for authentication processing.

    Note that each request will not need to be authenticated against the actual storage (in this case your LDAP server). You can configure Spring Security to cache this information, which means that the only first request will hit the LDAP server; any subsequent requests will be validated against the cached values.

    A custom implementation (like the one you are suggesting) will certainly be possible but it will pretty much require you to re-invent the existing functionality – obviously not a recommended way to go. If you really, really want to do the actual authentication in your own code rather than using the built-in implementation you can provide your own custom AuthenticationProvider implementation and plug that into the Spring Security configuration.

  6. shah

    Thanks again for your reply,
    It seems that i’m still struggling with this and I’m glad that there is someone out there that has already figured this out. I agree, that I don’t want to reimplment anything out of the box. Essentially i’ve gotten this far, I would appreciate your insight to see if i’m on the right approach.

    1.) I’ve implemented CommonsHttpInvokerRequestExecutor where i’m temporarily adding login info (i’ll move it out later). Just like you’ve done above. I don’t have access to a security context on the client side so i’m trying this.

    ublic class ExtendedInvoker extends CommonsHttpInvokerRequestExecutor {
    @Override
    protected PostMethod createPostMethod(HttpInvokerClientConfiguration config) throws IOException {
    PostMethod postMethod = super.createPostMethod(config);

    String user = “dianne”;
    String pw = “emu”;
    UsernamePasswordCredentials creds = new UsernamePasswordCredentials(user, pw);

    HttpState shtate=new HttpState();
    shtate.setCredentials(AuthScope.ANY, creds);
    return postMethod;
    }

    2. On the server side i’ve tried to use the namespace config as such:

    However now its asking for an entry point to be defined, before I start that next step, I wanted to know if I was on the right path. All I basically want to do is authenticate each remoting request via LDAP.

    I appreciate the help

  7. shah

    I’m sorry, I forgot to place the namespace config.

  8. shah

    I figured it out..thanks.
    I just created a new UsernamePasswordAuthenticationToken and placed it into a newly created securitycontext.

    Thanks for you help.

  9. Kyle

    One question: How do you expose the service over HTTPS? Do you edit something in the web.xml? I imagine its easy, but I’m new to all this.

  10. Mattias Hellborg Arthursson

    Exposing the service over HTTPS is no different from exposing any web application over HTTPS. It is a configuration matter quite unrelated to Spring Remoting. There’s basically two approaches – either you configure your web container (e.g. Tomcat) to expose the web applications over HTTPS, or – the general approach – you have your web application available as-is in Tomcat, and then put a web proxy in front, e.g. Apache.

    Regardless of the approach you choose it’s a configuration matter outside of the web application. Google will help you find the answer to that configuration question. Good luck.

  11. Sarah Kho

    Thanks for sharing the good knowledge.

    Can you please explain how we can have authorization in place? For example to only allow a certain role to invoke method1 or bean1

    Thanks.

  12. Val

    Hi
    Nice article, and it’s a great technic with spring.
    I don’t need ssl encryption but i have a problem with my firewall and load-balancer.

    The first communication is on HTTP for the connexion establishment but after, it’s just TCP binary data between client-server on random port for each remote call.
    There are no more data that the load-balancer can use to redirect to the good webserver.

    Is it possible to fix the port for the binary data for respect the firewall rules?
    And is it possible to add a ‘flag’ with the binary data (cookies, etc..) for the load-balancer?

    Thank.

  13. Chris

    I’m trying to do this with SSL and I used your BasicAuthenticationCommonsHttpInvokerRequestExecutor class and I still get the following error:

    org.springframework.remoting.RemoteAccessException: Could not access HTTP invoker remote service at [https://127.0.0.1:8443/my-app/MyService]; nested exception is javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

  14. Mattias Hellborg Arthursson

    Chris,
    You’re getting this exception because the certificate on the target server is not properly signed by a CA. There are a number of solutions to this problem; the correct one would obviously to use a signed certificate on the server.

    Other solutions (not recommended, but possible as workarounds) are to add the server certificate to your trusted certs on the client machine, or specify the certificate as a trusted certificate using the javax.net.ssl.trustStore system property.

  15. Ashton

    Hi there terrific blog! Does running a blog similar
    to this require a great deal of work? I have absolutely no knowledge of programming but I
    had been hoping to start my own blog soon. Anyways, if you have any suggestions or techniques for new blog owners
    please share. I know this is off subject however I
    just needed to ask. Kudos!

  16. abby

    i tried above authentication technique but gives
    org.springframework.remoting.RemoteAccessException: Could not access HTTP invoker remote service

    i could not find why the authentication is blocking spring remoting

Leave a Reply