The Source for Java Technology Collaboration
User: Password:



Craig Castelaz

Craig Castelaz's Blog

Buried treasure can be found nearly anywhere

Posted by castelaz on July 14, 2004 at 04:30 AM | Comments (4)

It started as a typical morning. I was three days behind schedule on a one day project, but hoping to finish it by late afternoon. Of course, I’d no sooner gotten into the code, trying to recapture yesterday’s flow, when the phone rang. The next minute, I was off to my project manager’s office, no doubt to receive yet another reason to delay my current assignment even more.

Now, don’t get me wrong. I’m not complaining. I happen to have a great project manager. He understands that the to-do list is infinitely expandable, but the workday isn’t. He recognizes that if he pulls me from one task to work another, the one left behind isn’t going to magically complete itself. His attitude keeps us both sane. Besides, I like variety, and enjoy having several projects to switch to when I grow stale on one.

Sure enough, I did indeed get a new assignment. I had to write code to login to a web site protected by digest authentication. Fortunalely, I had worked on a fairly large and reasonably sophisticated authentication/authorization component in the past, and was familiar with the various web login methods. While I had never actually implemented digest authentication before, I had some understanding of how it worked. So, after meeting with one of our network administrators, who helped me setup a secured site, we did a bit of packet sniffing of the digest authenticated site we just created. The packet exchange pretty much matched my general understanding of how authentication process worked, and it didn’t look like it would be all that difficult to implement.

Fortunately, before writing any code, my innate laziness asserted itself, if laziness can ever assert itself, and I took a little time to look for something I could use as a starting point. Things were looking pretty grim, until I hit upon Jakarta Commons’ HttpClient. While Simon Brown has already blogged on this same subject, HttpClient - another great Jakarta Commons component, his discussion was limited to the component’s Post method. Additionally, HttpClient is such a useful gem, it bears mentioning again-and-again.

As I just said, HttpClient is part of the Jakarta Commons. The primary goal of the Commons is to create and maintain a repository of reusable Java components which are free and open source. Although I haven't had a wide exposure to all the components the Commons offers, the ones I have used have been a real pleasure. This was particularly true for HttpClient. Unlike a lot of open source, HttpClient has a fairly extensive User Guide, and between it and a few samples, I was able to put together a digest authentication method in relatively short order. Certainly, faster than I would have been able to write a bare-bones, untested implementation myself.

public void access(HttpURL httpURL) throws Exception {
	try {
		String siteUsername = httpURL.getUser();
		String sitePassword = httpURL.getPassword();
		HttpClient client = new HttpClient();
		if ((siteUsername != null ) && (sitePassword != null)) {
			client.getState().setCredentials("Realm", jnlpURL.getHost(), 
                            new UsernamePasswordCredentials(siteUsername, sitePassword));
		}
		HttpMethod method = new GetMethod(httpURL.getURI());
		try {
			int statusCode = client.executeMethod(method);
			String statusStr = HttpStatus.getStatusText(statusCode);
			if (statusStr.equalsIgnoreCase("unauthorized")) {
				throw new Exception("Not authorized for realm");
			}
			if (statusStr.equalsIgnoreCase("not found")) {
				throw new Exception("Webapp and/or file not found");
			}
			doSomethingUseful(method.getResponseBodyAsStream());
			method.releaseConnection();
		}
		catch (HttpRecoverableException hre) {
			throw new Exception("Exception '" + hre.toString() + "');
		}
		catch (UnknownHostException uhe) {
			throw new Exception("Exception '" + uhe.toString() + "');
		}
	}
	catch (URIException urie) {
		throw new Exception("Exception '" + urie.toString() + "');
	}
	catch (IOException e) {
		throw new Exception("Exception '" + e.toString() + "');
	}
}

There really isn't much to say about the code. The HttpURL object passed into the access method can be based upon a URL which has an embedded username and password. If you've never seen the syntax for such a URL, http://craig:foobar@host.com/webapp has an embedded username of craig and password of foobar. If the username and password are available, the access method establishes credentials based upon the username and password pulled from the URL. It then performs a standard HTTP GET. This is where the authentication magic occurs. Without going into a lot of detail, the web server detects the presence of the credentials and attempts to authenticate the client. If all goes well, and the credentials are accepted, the client is silently admitted to the protected site. The various status checks and exception catches throughout the access method are there to give you an indication of what can go wrong.

The only warning I need to give regarding the authentication process is that the password needs to be digested prior to being embedded in the URL. Since the site is protected by digest authentication, it stands to reason that the password needs to be digested before being transmitted to the web server. The encryption of the password is typically based the MD5 Digest. A short article explaining Java 2 Security with sample code using MD5 can be found at Java security evolution and concepts, Part 2.

Compared to all that I would have had to write, HttpClient saved me a considerable amount of time and grief. Jakarta Commons offers many other useful components. Check it out !


Bookmark blog post: del.icio.us del.icio.us Digg Digg DZone DZone Furl Furl Reddit Reddit
Comments
Comments are listed in date ascending order (oldest first) | Post Comment

  • One Correction
    There is one correction I must mention to the code above - the call to releaseConnection() *must* always be called regardless of whether or not an exception is thrown so it should actually be in a finally block. Sorry, I seem to have made the mistake when I wrote the tutorial. It won't matter with the default connection manager, but will if you switch to the MultiThreaded connection manager.

    Posted by: ajsutton on July 14, 2004 at 07:34 AM

  • A Buried Treasure Indeed
    The Jakarta Commons project is indeed such a buried treasure. It is a shame that so many developers aren't using it.
    I succesfully managed to convince an internal development team to use the HttpClient component. It only took 3 weeks of wasted time and efforts though. Once they adopted it, they were up and running in 2 days.
    My current battle is to get the same developers to adopt the FileUpload component. It's still the same battle. They did make it work without, but they did spend days on their homegrown version.
    Oh well... Sometimes you win, sometimes you loose.
    Frederic Jean

    Posted by: fredjean on July 15, 2004 at 01:57 AM

  • One Correction
    Thanks for the correction. I've made the necessary changes in my "production" code.

    Posted by: unknown1 on July 15, 2004 at 06:38 AM

  • A Buried Treasure Indeed
    The Not Invented Here Syndrome continues to thrive. Best of luck in getting them over it

    Craig

    Posted by: castelaz on July 15, 2004 at 11:22 PM





Powered by
Movable Type 3.01D
 Feed java.net RSS Feeds