Tuesday, October 18, 2011

Using Browser Web Services with SSO


A while ago I discussed how web services can be invoked using javascript in a browser, but there was a nagging problem that prevented me from deploying it into a production environment - how do I prevent a hacker from manipulating the user identifier in a request? Suppose we have an application that displays an end user's payroll information. In this case we need web service operation will retrieve information specific to the user's ID. We can't just send the user's identifier in the request because that would allow spoofing the ID of another person resulting in a breach of privacy.

Retrieving the User's Identity from the Container
In a servlet, it is relatively easy to retrieve the user's identity from the request, but this presents a problem if we're using JAX-WS annotated POJOs which have no concept of HTTP or a request object. How the heck do I figure out what the user ID is? I spent many hours looking at WS-Security but I got lost in all the details about policy sets and bindings and I just couldn't get anything to work. Then I found this writeup on developerworks:
http://www.ibm.com/developerworks/websphere/library/techarticles/1001_chung/1001_chung.html?ca=drs-

It is very close to what I wanted to do, but it uses WS-Security and I was dead in the water. Instead, I fell back on servlet security configuration to get the container to use the SSO LTPA token originating from WebSeal. This is the same way I would handle the situation if I was coding a servlet, but again - I'm working with POJOs, so no request object available. The developerworks article revealed the missing piece:
@Resource WebServiceContext wsCtx;

Putting it all Together
I coded a POJO and added JAX-WS annotations to expose it as a web service, then I added an @Resource that tells the container to pass the context:
package com.dsixe;

import javax.annotation.Resource;
import javax.jws.WebService;
import javax.xml.ws.WebServiceContext;

@WebService
public class ContactsService {
    @Resource WebServiceContext wsCtx;

    public String getName() {
        String userID = wsCtx.getUserPrincipal().toString();
        
        if (userID.equals("cdomingue"))
            return "Carl";
        if (userID.equals("csmith"))
            return "Christine";
        
        return "unknown: " + userID;
    }
    
}
Until this point I had never even looked at the web.xml bundled with the annotated POJOs into the WAR that I deployed to Axis2 since there are no servlets in it. I added the following security constraints to the web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
  <display-name>Jax-wsPolicyTest</display-name>
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
  </welcome-file-list>
  
  <security-constraint>
    <web-resource-collection>
      <web-resource-name>ContactsService</web-resource-name>
      <url-pattern>
        /*
      </url-pattern>
    </web-resource-collection>
    <auth-constraint>
      <description>
        Roles allowed to execute ContactsService
      </description>
      <role-name>AllAuthenticatedUsers</role-name>
    </auth-constraint>
  </security-constraint>
  
  <security-role>
    <description>All Authenticated Users Role</description>
    <role-name>AllAuthenticatedUsers</role-name>
  </security-role>  
</web-app>
I then deployed to the container and mapped the AllAuthenticatedUsers security role just like I would for any servlet. Protecting the resource caused the container to populate the WebServiceContext object and then I could retrieve the user ID.

Done!

EDIT: I was researching attachments with JAX-WS and found this very comprehensive writeup which I though would be good to read in this context.

EDIT: Here's another interesting read on this topic.

Please note that generic comments containing links with the intent of self promotion will be flagged as spam and deleted.

0 comments:

Post a Comment

Are you about to post a generic comment that has nothing to do with this post? Something like "Hey thanks for this very valuable information, BTW here's my website". If so, it will be marked as spam and deleted within 24 hours.

Note: Only a member of this blog may post a comment.