Servlet Security for Web Services

Irrespective of what API a Web service client uses, it eventually creates a SOAP message and posts it, using HTTP POST, to the service address URL. This message is picked up by the Tomcat Web container and delivered to the Axis servlet. Axis, after doing its own processing and conversions, invokes the appropriate service implementation code. So, in its guts, interaction between a client program and Web service is not very different from the way a Web browser interacts with a Servlet-based Web application deployed within a Web container.

So you should not be surprised to learn that it is possible to make use of Servlet security mechanisms, as explained in Chapter 9, Web Application Security, to authenticate the client to the server and control access to service address URLs, and the Web services themselves. We look at the specifics of doing so in this section.

As we have seen, that the service address URLs for Web services deployed within Axis have the format: http://hostname:port/axis/services/servicename. By putting proper declarations in the Web application deployment descriptor for Axis, i.e., file web.xml in directory %TOMCAT_HOME%webappsaxisWEB-INF, we can specify URL patterns that require user login. Shown below are the declarative statements to allow only Tomcat users with the role "StringEchoPort1UserRole" to access Web service StringEchoPort1 of our previous example.

<security-constraint>
  <web-resource-collection>
    <web-resource-name>Web service StringEchoPort1</web-resource-name>
    <url-pattern>/services/StringEchoPort1</url-pattern>
  </web-resource-collection>
  <auth-constraint>
    <role-name>StringEchoPort1UserRole</role-name>
  </auth-constraint>
</security-constraint>

<login-config>
  <auth-method>BASIC</auth-method>
  <realm-name>Axis Basic Authentication Area</realm-name>
</login-config>

<security-role>
  <role-name>StringEchoPort1UserRole</role-name>
</security-role>

You can see that the deployment descriptor specifies HTTP-Basic authentication through the auth-method subelement of the login-config element. Although HTTP-Basic is used here, you could use HTTP-Digest as well (provided the Web container supports it). FORM-based authentication, because it relies on showing a login page through the Web browser, is not well suited for a program client and is not advised. Client Certificate-based authentication is also a possibility. We talk about it later in the section SSL Security for Web Services.

To apply access control to StringEchoPort1 Web service, insert these declarations within the web-app element of the Axis web.xml file at the appropriate location and setup Tomcat user database with a role named StringEchoPort1UserRole and assign this role to users who you want to access the service. Refer to Chapter 9, Web Application Security, for for details on how to carry out these steps.

After you have modified the Axis web.xml and have setup a user with StringEchoPort1UserRole role, try to access the service WSDL through a Web browser, either by directly entering the WSDL URL or by clicking the View link on the Axis Welcome Page and following the link for StringEchoPort1 service WSDL. The Web browser should throw up a login panel and demand a username and password. Once you supply them, the Web browser should display the WSDL document.

With a Web browser, we could simply enter the username and password through a UI element, but how are we going to specify these values from within a client program? The JAX-RPC specification defines two properties, Call.USERNAME_PROPERTY and Call.PASSWORD_PROPERTY, which can be set in a Call object. If these properties are set, JAX-RPC client runtime system takes care of HTTP protocol-level details to specify proper HTTP headers for authentication. The following code fragment shows how the client program EchoClient.java has to be modified to use username and password. The complete working program is in source file EchoClient1.java within the same directory.

						String wsdlAddr = "file:test.wsdl";
// ... skip
Call call = (Call) svc.createCall();
// ... skip
// arg: String variable initialized with string to be sent
// username: String variable initialized with username
// password: String variable initialized with password
call.setProperty(Call.USERNAME_PROPERTY, username);
						call.setProperty(Call.PASSWORD_PROPERTY, password);

String res = (String) call.invoke(new Object[] {arg});

Did you notice that besides setting the Call properties, we have also changed the initialization value for wsdlAddr variable to a URL pointing to a local file? This is required because the createService() method of ServiceFactory (refer to source file EchoClient.java shown in Listing 11-3) attempts to retrieve the WSDL document from the specified URL. If you specify the URL served by the Axis, then the retrieval will fail because there is no way to specify username and password for this access. This appears to be a limitation of using Servlet-based security and the way WSDL URL is created by Axis. As a work-around, you can retrieve the WSDL through some other means and store it in a local file. For example, you can get the WSDL document through your Web browser and save it in a file. This is what we have done.

Can the service program access the username supplied by the client program? We know from our discussion in Chapter 9, Web Application Security, that the class HttpServletRequest has the methods getRemoteUser() and getUserPrincipal() to retrieve user information within a Servlet-based Web application. So, essentially what we need is the ability to access the HttpServletRequest instance within a service class. It turns out that this is possible, at least with Axis. This technique is illustrated in the source code of class DisplayUserInfo.java.

// File: srcjsbookch11ex1DisplayUserInfo.java
import org.apache.axis.MessageContext;
import org.apache.axis.transport.http.HTTPConstants;
import javax.servlet.http.HttpServletRequest;

public class DisplayUserInfo {
  public static void display() {
    MessageContext context = MessageContext.getCurrentContext();
    HttpServletRequest req = (HttpServletRequest)
        context.getProperty(HTTPConstants.MC_HTTP_SERVLETREQUEST);
    System.out.println("remote user = " + req.getRemoteUser());
    System.out.println("remote principal = " + req.getUserPrincipal());
  }
}

Note that the method display() relies on the static method getCurrentContext() of org.apache.axis.MessageContext class to get the MessageContext instance. Armed with this, it gets hold of the HttpServletRequest instance corresponding to the service request by getting the property value of the Axis-specific property HTTPConstants.MC_HTTP_SERVLETReQUEST. Once you have the HttpServletRequest instance, getting the remote user name is straightforward. You can invoke the static method display() of DisplayUserInfo within the body of any service class implementation. But keep in mind that this code works only with Axis.

Though we have illustrated the use of Servlet-based security for client authentication to a Web service with the Axis and Tomcat, this technique is fairly general and applies to all Web services that are deployed within a Web container and are HTTP accessible.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.146.206.116