JAX-WS Basic authentication

User Rating: 5 / 5

Star ActiveStar ActiveStar ActiveStar ActiveStar Active
 

webservicesIn the context of an HTTP transaction, BASIC access authentication is a method for a web browser or other client program to provide a user name and password when making a request.
This tutorial shows how to perform BASIC authentication using Apache CXF Interceptors and, as alternative, the JAX-WS WeServiceContext.


One advantage of the BASIC access authentication is that all web browsers support it. It is often used by Intranet private systems, where it’s not necessary a strict security policy.

How are credentials encoded with HTTP Basic authentication ?
Before transmission, the user name is appended with a colon and concatenated with the password. The resulting string is encoded with the Base64 algorithm. For example, given the user name 'Aladdin' and password 'open sesame', the string 'Aladdin:open sesame' is Base64 encoded, resulting in 'QWxhZGRpbjpvcGVuIHNlc2FtZQ=='.

Where are credentials stored ? not in the SOAP packet but in the HTTP Header.
Here is a DUMP of a SOAP request using BASIC Authentication:

POST /windcpms/ws/CfmsInterfaceService HTTP/1.1
Content-Type: text/xml; charset=UTF-8
Accept: */*
Authorization: Basic YWRtaW5BQUE6dGVzdG53aw==
SOAPAction: ""
User-Agent: Apache CXF 2.5.0
Cache-Control: no-cache
Pragma: no-cache
Host: 127.0.0.1:8080
Connection: keep-alive
Content-Length: 368

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>. . . . .</soap:Body>
</soap:Envelope>


That being said, there are several ways to achieve basic authentication. If you are using Apache CXF, a very simple and effective way is adding an Interceptor which is triggered when your Web services are Invoked.
Here is a sample one:

package com.sample.ws;

import java.io.IOException;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import org.apache.cxf.binding.soap.interceptor.SoapHeaderInterceptor;
import org.apache.cxf.configuration.security.AuthorizationPolicy;
import org.apache.cxf.endpoint.Endpoint;
import org.apache.cxf.interceptor.Fault;
import org.apache.cxf.message.Exchange;
import org.apache.cxf.message.Message;
import org.apache.cxf.transport.Conduit;
import org.apache.cxf.ws.addressing.EndpointReferenceType;

 

 

public class BasicAuthAuthorizationInterceptor extends
 SoapHeaderInterceptor {   

  @Override
  public void handleMessage(Message message) throws Fault {

  AuthorizationPolicy policy = message.get(AuthorizationPolicy.class);
 
 
  // If the policy is not set, the user did not specify credentials.
  // 401 is sent to the client to indicate that authentication is required.
  if (policy == null) {
    sendErrorResponse(message, HttpURLConnection.HTTP_UNAUTHORIZED);
  return;
  }

  String username = policy.getUserName();
  String password = policy.getPassword();


 // CHECK USERNAME AND PASSWORD
  if (!checkLogin(username,password)) {
    System.out.println("handleMessage: Invalid username or password for user: " +   policy.getUserName());
  sendErrorResponse(message, HttpURLConnection.HTTP_FORBIDDEN);
  }
 }

  private boolean checkLogin(String username, String password) {
   if (username.equals("admin") && password.equals("admin")) {
    return true;
  }
   return false;
  }

  private void sendErrorResponse(Message message, int responseCode) {
   Message outMessage = getOutMessage(message);
   outMessage.put(Message.RESPONSE_CODE, responseCode);
 
   // Set the response headers
   @SuppressWarnings("unchecked")
   Map<String, List<String>> responseHeaders =  (Map<String, List<String>>)    message.get(Message.PROTOCOL_HEADERS);
 
   if (responseHeaders != null) {
     responseHeaders.put("WWW-Authenticate", Arrays.asList(new String[] { "Basic realm=realm" }));
     responseHeaders.put("Content-Length", Arrays.asList(new String[] { "0" }));
  }
  message.getInterceptorChain().abort();
   try {
    getConduit(message).prepare(outMessage);
   close(outMessage);
   } catch (IOException e) {
      e.printStackTrace();
   }
 }

  private Message getOutMessage(Message inMessage) {
   Exchange exchange = inMessage.getExchange();
   Message outMessage = exchange.getOutMessage();
   if (outMessage == null) {
    Endpoint endpoint = exchange.get(Endpoint.class);
    outMessage = endpoint.getBinding().createMessage();
    exchange.setOutMessage(outMessage);
   }
    outMessage.putAll(inMessage);
     return outMessage;
    }

  private Conduit getConduit(Message inMessage) throws IOException {
   Exchange exchange = inMessage.getExchange();
   EndpointReferenceType target = exchange.get(EndpointReferenceType.class);
   Conduit conduit = exchange.getDestination().getBackChannel(inMessage, null, target);
   exchange.setConduit(conduit);
   return conduit;
  }

  private void close(Message outMessage) throws IOException {
   OutputStream os = outMessage.getContent(OutputStream.class);
   os.flush();
   os.close();
  }
 
}

The above interceptor needs to be registered either with a simple annotation at Class level of your Web service:


@InInterceptors(interceptors = "com.sample.ws.BasicAuthAuthorizationInterceptor")

or using Apache CXF configuration file:

<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xmlns:cxf="http://cxf.apache.org/core"
 xmlns:jaxws="http://cxf.apache.org/jaxws"
 xsi:schemaLocation="
 http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
 http://cxf.apache.org/core http://cxf.apache.org/schemas/core.xsd
 http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd">

 <import resource="classpath:META-INF/cxf/cxf-all.xml" />

 <cxf:inInterceptors>
   <ref bean="basicAuthAuthorizationInterceptor"/>
 </cxf:inInterceptors>

 </cxf:bus>

 <bean id="basicAuthAuthorizationInterceptor"  class="com.sample.ws.BasicAuthAuthorizationInterceptor">
 </bean>
</beans>

 



Another option is using the @WebServiceContext to access the Authorization parameter contained in the Header. This is a portable solution although it needs a bit more of work to decode the credentials to Base64 to plain text.

 

 

This requires Apache commons-codec libraries. You can download them here:

 

http://commons.apache.org/codec/

 

Authentication can be performed using a simple function:


byte[] buf = Base64.decodeBase64(userpass.getBytes());

 

Here’s the full code:

@WebService
public class SecuredWSImpl implements SecuredWS {

 @Resource
 WebServiceContext wsctx;
 
 
 public void doSomething () {

 doAuthentication();
 
 // Execute WS business logic
 }
 
 private void doAuthentication() {

 

 

 MessageContext mctx = wsctx.getMessageContext();
 Map http_headers = (Map) mctx.get(MessageContext.HTTP_REQUEST_HEADERS);
 
  ArrayList list = (ArrayList) http_headers.get("Authorization");
  if (list == null || list.size() == 0) {
    throw new RuntimeException("Authentication failed! This WS needs BASIC Authentication!");
 }
 
  String userpass = (String) list.get(0);
  userpass = userpass.substring(5);
  byte[] buf = Base64.decodeBase64(userpass.getBytes());
  String credentials = new String(buf);
 
  String username = null;
  String password = null;
  int p = credentials.indexOf(":");
  if (p > -1) {
   username = credentials.substring(0, p);
   password = credentials.substring(p+1);
  }   
  else {
   throw new RuntimeException("There was an error while decoding the Authentication!");
  }
  // This should be changed to a DB / Ldap authentication check 
  if (username.equals("admin") && password.equals("admin")) { 
  System.out.println("============== Authentication OK =============");
  return;
  }
  else {
   throw new RuntimeException("Authentication failed! Wrong username / password!");
  }
 
 }
 
}


Advertisement

Related articles available on mastertheboss.com

JBoss web services

JAX-WS simplifies the development model for a web service endpoin

What is a Web Service One Way invocation?

JBoss recipe of the day

Asynchronous web services with JBoss WS

Developing rigorous and responsive web service client application

How to change the default Web Service deployment Port ?

JBoss recipe of the day

Using Axis Web Services with JBoss

Still not ready for JBoss WS ? if you don't have a JDK 1.5 compli

Invoking JBoss Web Services with Flex

Flex® is a free, open source framework for building highly in