Monday, September 15, 2008

Form-based Authentication and Apache HttpClient

A month ago I was asked by my boss to create a login page using form based authentication (j_security_check) and authenticate with Weblogic Server, but in the middle I wanted to intercept the process to get the user id and password for me to store into an ALBPM sessions. The solution that I wanted to explain is using Apache HttpClient.

In the web development, we know that we can protect our website through authentication mechanism. Some of the authentication mechanisms are:
1. HTTP basic authentication
2. Form-based login authentication
3. Client certificate authentication
4. Mutual authentication
5. Digest authentication


Most of the web containers are supports this authentication mechanism like Apache Tomcat, Sun Glassfish, BEA Weblogic, IBM Websphere and so on. Form-based authentication is a login page that has to be an HTML form that has a POST action named j_security_check, an input element named j_username, and a password element called j_password.

Below is the sample of login page using Form-based authentication:

<form method=post action="j_security_check" >
<input type="text" name= "j_username" >
<input type="password" name= "j_password" >
<input type="submit" value= "Submit" >
</form>

For the diagram process, you can refer to:
http://java.sun.com/j2ee/1.4/docs/tutorial/doc/Security5.html

After I can get the user id and password, I encountered a problem how to forward back the user id and password to j_security_check in Weblogic Server?.

In java servlet we know that we can use request or response object to forward or redirect a particular page to another page. The request object that we are talking about is request.getRequestDispatcher and the response object is response.sendRedirect.

When I am using request.getRequestDispatcher() to forward, it gave ‘Page Not Found’ using the example below:
request.getRequestDispatcher("j_security_check?j_username="+j_username+"&j_password="+j_password)


and if I am using response.sendRedirect using the example below, it can successfully went through but the problem is you can see clearly the user id and password in the browser address bar.
response.sendRedirect("j_security_check?j_username=" + j_username + "&j_password=" + j_password)

I was surprised and thinking how to hide the user id and password? At least I already one step forward, until I found a way to perform HTTP POST request using Apache HttpClient (http://hc.apache.org/httpclient-3.x/), so the user id and password can be send implicitly exactly as normal login page (without interception).

String LOGON_SITE = request.getServerName();
int LOGON_PORT = request.getServerPort();
HttpClient client = new HttpClient();

client.getHostConfiguration().setHost(LOGON_SITE, LOGON_PORT, "http");
client.getParams().setCookiePolicy("compatibility");
String jSecurityCheckUri = null;

if(jSecurityCheckUri == null)
{
String ctxPath = request.getContextPath();
jSecurityCheckUri = (new StringBuilder(String.valueOf(ctxPath))).append("/j_security_check").toString();
}

String j_username = urlEncode(request.getParameter("j_username"));
String j_password = urlEncode(request.getParameter("j_password"));
String v_submit = urlEncode(request.getParameter("submit"));
PostMethod authpost = new PostMethod(jSecurityCheckUri);
NameValuePair userid = new NameValuePair("j_username", j_username);
NameValuePair password = new NameValuePair("j_password", j_password);
authpost.setRequestBody(new NameValuePair[] {
userid, password
});

int statuscode = 0;
client.executeMethod(authpost);
authpost.releaseConnection();
statuscode = authpost.getStatusCode();

//redirect to any URL
if(statuscode == HttpStatus.SC_MOVED_TEMPORARILY ||
statuscode == HttpStatus.SC_MOVED_PERMANENTLY ||
statuscode == HttpStatus.SC_SEE_OTHER ||
statuscode == HttpStatus.SC_TEMPORARY_REDIRECT)
{
Header header = authpost.getResponseHeader("location");
if(header != null)
{
String newuri = header.getValue();
if(newuri == null || newuri.equals(""))
{
newuri = "/";
}
String uri = newuri.split(";", 2)[0];
uri = (new StringBuilder(String.valueOf(uri))).append("").toString();

GetMethod redirect = new GetMethod(uri);
int resCode = client.executeMethod(redirect);

String outputStr = redirect.getResponseBodyAsString();

redirect.releaseConnection();

// Print output to servlet
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.print(outputStr);
out.flush();
out.close();
}
else
{
System.out.println("Invalid redirect");
}
}



I hope the article above will can be useful.