Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

JVM Languages

SAML, JAAS, & Role-Based Access Control: Part 2


Web service Client Security: Approach and Configuration

Step 1: NT login configuration

The approach uses a JAAS NT login module. Login modules are identified by a name in a configuration file (see Listing One).


GetNTLoginModule  {
com.sun.security.auth.module.NTLoginModule required; 
};

Listing One: jaas.conf configuration file

Most of these modules expect to be run interactively by a user either from an application or on the command line. The argument -D java.security.auth.login.config=jaas.conf is required on the JVM. Essentially, this authenticates a user (who has already logged into NT) within a Java application. The LoginContext object constructor instantiates itself as an NT login (see Listing Two).


lc = new LoginContext("GetNTLoginModule");
lc.login();

Listing Two: JAAS login within the Java application

Step 2: Token acquisition from a SAML provider

For Web services security, a remote call must be made to a SAML provider in order to get the signed, XML token. Essentially, Listing Three should be viewed as an interface method that needs to be implemented, based on the SAML provider in your architecture.

After the token is acquired, it should be stored within a String object. A cached assertion can be reused for subsequent Web service calls during the lifetime of the client session. However, for long running clients, the SAML assertion may expire, which will result in a server-side SOAP fault.

The Java application should be designed to specifically catch an expired assertion fault and force an automatic acquisition of a new token. In the vast schema of things, expiration dates could be set such that they may not expire to well into the future. In effect, though, this type of policy would represent weak management and a breach of best practices.

However, technically speaking, the SOAPMessage object provides behavior for persisting a SOAP message to a local file (see Listing Six) and the application could be designed to be reentrant from a security standpoint. Needless to be said, just because something is viable doesn't mean it is a good thing to do.


String assertion = asserter.getAssertion();

Listing Three: Storing a SAML token as a String object

While the asserter behavior is implementation-specific, the XML response needs to be well-formed XML assertion (see Listing Four). Note that this assertion is specified to use groups, which will allow the application server to secure the Web service by role.

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"> 
<soapenv:Header> 
<saml:Assertion AssertionID="_a1ddddfcd0e6ca80f093d9562ce26b39" ID="_a1ddddfcd0e6ca80f093d9562ce26b39" IssueInstant="2008-03-28T17:54:59.59Z" Issuer="http://xyz.abc.com" MajorVersion="1" MinorVersion="1" xmlns:saml="urn:oasis:names:tc:SAML:1.0:assertion"> 
 ...
<saml:Attribute AttributeName="Groups" AttributeNamespace="urn:bea:security:saml:groups"> 
<saml:AttributeValue>Customer_Accounting</saml:AttributeValue> 
</saml:Attribute> 
<saml:Attribute AttributeName="Groups" AttributeNamespace="urn:bea:security:saml:groups"> 
<saml:AttributeValue>Customer_Service</saml:AttributeValue> 
</saml:Attribute> 
 ...
</saml:Assertion> 
</soapenv:Header> 
 ...
</soapenv:Envelope>
Listing Four: SAML Assertion (Token)

Step 3: Security preprocessing steps before invoking the Web service

The "secure" operation method is a method on the Java application which is responsible for:

  • Installing the callback registry handler that sets the SAML token into the SOAP message header;
  • Setting properties, such as port and namespace;
  • Invoking the Web service.

It is important to point out that for a production applications, handler implementation-details should be encapsulated in a set of objects outside of the programmer space.

An explanation of the processing steps is embedded as comments in Listing Five.

private void secureOperation( Service_Impl service, Hello port,  Stub helloStub, String assertion) {

    try {
/* Get the handler registry from the service. */

HandlerRegistry hr = service.getHandlerRegistry();

/* Create hash map (i.e. config) that will be used to store initialization parameters for client-side handler. */

HashMap<String,String> config = new HashMap<String,String*gt;();

/* The assertion is retrieved by the application at start up and stored as a class or static object. */

config.put("SAML_ASSERTION", assertion);

/* Create HandlerInfo object, which will store the config object. */

HandlerInfo hi = new HandlerInfo();
hi.setHandlerConfig(config);

/* Set the SAMLAuthenticationHandler.class into the HandlerInfo object. */
 
hi.setHandlerClass(SAMLAuthenticationHandler.class);

/* Create an ArrayList and add the HandlerInfo object to it. */

ArrayList<HandlerInfo> handlerChain = new ArrayList<HandlerInfo>();
handlerChain.add(hi);

/* On the HandlerRegistry set the Target namespace, SOAP service port name 
and Arraylist that contains the config and the AuthenticationHandler. */

hr.setHandlerChain(new QName(_properties.getProperty("targetNamespace"),
_properties.getProperty("portName")), 
handlerChain);

/* Invoke the Web service which now contains a SAML assertion in the header of the message. */
String result = port.secureServiceMethod ());
} catch (Throwable throwable) { 
 ...
}}
Listing Five: Secure Web service method

Step 4: Injecting the SAML token into the SOAP header

In real-time, the SAMLAuthenticationHandler object has completed processing before the Web service is invoked in Listing Five. The SAMLAuthenticationHandler is responsible for setting the SAML token into the SOAP header.

An explanation of the processing steps is embedded as comments in Listing Six.

public class SAMLAuthenticationHandler extends GenericHandler {

	private String assertion = null;
	private static final String NS_WSSE = 
"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd";	
	private SOAPFactory sf = null;
	private QName[] headers;
	
	public SAMLAuthenticationHandler() throws SOAPException {
		super();
		sf = SOAPFactory.newInstance();
}	

	public void init(HandlerInfo handlerInfo) {		
		super.init(handlerInfo);
		Map config = handlerInfo.getHandlerConfig();

		/* Get the assertion that is stored in the Config object */

		assertionString = (String) config.get("SAML_ASSERTION");		
	}	

	public QName[] getHeaders() {
		
		return headers;
	}
	
	public boolean handleRequest(MessageContext context) {
		
	    try {
/* Bind SAML token into security head by instantiating a message context */
 	
		SOAPMessageContext messageContext = (SOAPMessageContext) context;
		
		/* Instantiating a message context */
	
SOAPMessage message = messageContext.getMessage();	
		
		/* Create an input stream, based on the assertion */
	
		InputStream bin = new ByteArrayInputStream(assertionString.getBytes("UTF-8"));
		  		
		/* Get a SOAP header */

		SOAPHeader header = message.getSOAPPart().getEnvelope().getHeader();
		
		/* Add a schema statement to the header */

		SOAPElement securityHeaderElement = 
header.addChildElement(sf.createName("Security", "wsse", NS_WSSE));

		/* Add a namespace statement to the header */ 
		securityHeaderElement.addNamespaceDeclaration("wsse", NS_WSSE);	

/* Get a document element form the assertion */

Node assertionNode = DocumentBuilderFactory.newInstance().newDocumentBuilder().
parse(new ByteArrayInputStream(assertion.getBytes())).getDocumentElement();
			
		bin.close();	
			
		/* Add the SAML assertion to the header */

securityHeaderElement.addChildElement(convert(assertionNode));
            		            
            		/* Optionally, for testing purposes write message to file for testing purposes */

            		message.writeTo(new FileOutputStream("XMLRequest1.xml")); 		

	    } catch (Exception e1) {
		...		
	   }
		
		return true;
	}
	
	/* The methods below convert and copy are SOAP helper processing methods */

	private SOAPElement convert(Node element) throws SOAPException {
	
    SOAPElement soapElement  = sf.createElement(element.getNodeName(), 
element.getPrefix(), element.getNamespaceURI());
	    copy(element, soapElement);
	    return soapElement;
	
}
	
	private void copy(Node source, SOAPElement target) throws DOMException, SOAPException {
		
	    for (int j = 0; j < source.getAttributes().getLength(); j++) {
		Node node = source.getAttributes().item(j);
			
		String name = node.getNodeName();
		if (name.startsWith("xmlns")) {
		    int index = name.indexOf(":");
		    if (index < 0) {//default namespace
			target.addNamespaceDeclaration("", node.getTextContent());
		    } else {
			target.addNamespaceDeclaration(name.substring
("xmlns:".length()), node.getTextContent());
		    }
		} else {
		    target.addAttribute(sf.createName(name, 
node.getPrefix(), node.getNamespaceURI()), node.getTextContent());
		}
                    }		
			    for (int i = 0; i < source.getChildNodes().getLength(); i++) {
		Node node = source.getChildNodes().item(i);
			
		if (Node.ELEMENT_NODE == node.getNodeType()) {
		//copy element
		    SOAPElement targetElement = target.addChildElement(
                                            node.getNodeName(), node.getPrefix(), node.getNamespaceURI());
				
				//copy subelements
				copy(node, targetElement);				
		} else if (Node.TEXT_NODE == node.getNodeType()) {
				//copy text nodes
				target.addTextNode(node.getTextContent());
		}			
	   }
              }
}
Listing Six: SAML authentication handler

Implications

In practice, most of the code in this article should be abstracted away from Java application programmers. Essentially, information contained in SOAP objects is opaque. The size and shape of opaque objects should not be directly accessible to the user. Opaque objects should be accessed via handles, which exist in user space.

Even issues such as exception handling for expired tokens are system programmer's responsibility, not application programmers. As is the case with all security-related programming, the objective is to keep the application programmer productive, while enforcing the organizations security polices.


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.