27 June 2007

Accessing j_username in OC4J form based authentication failures

A question on the OC4J OTN forum recently asked about how the error page used in form based authentication could get access to the username that was provided so an audit trail could be established.

Intuitively you'd expect that the form fields passed in from the logon form would be passed through to the error page when an authentication fails and the forward is done. However this is not the case. The request parameter map is empty.

The solution is to use an OC4J proprietary feature called a Form Auth Filter. This is a standard Servlet Filter that can be injected into the request path when form based authentication is performed. The filter will be called before the authentication is performed and has access to the full set of request parameters passed in from the j_security_check form.

http://download-west.oracle.com/docs/cd/B32110_01/web.1013/b28959/filters.htm#sthref150

To accomplish the task of making the supplied j_username available in the error page, a form auth filter can extract the j_username parameter and store it in the request as an attribute.

Then in the error handler defined for the form-auth (jsp, struts action, etc.) simply extract the request attribute and do what you want with it.

Here's a step by step example.

1. Create a web application that uses form-based-authentication

<login-config>
<auth-method>FORM</auth-method>
<form-login-config>
<form-login-page>web-inf/logon.jsp</form-login-page>
<form-error-page>web-inf/error.jsp</form-error-page>
</form-login-config>
</login-config>

2. Create a ServletFilter to remap the j_username request parameter

package demo.sab.otn.formauthfilter;

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class FormAuthFilter implements Filter {
private FilterConfig _filterConfig = null;

public void init(FilterConfig filterConfig) throws ServletException {
_filterConfig = filterConfig;
}

public void destroy() {
_filterConfig = null;
}

public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletResponse res = (HttpServletResponse)response;
HttpServletRequest req = (HttpServletRequest)request;

String j_username = req.getParameter("j_username") ;
if(j_username!= null) {
req.setAttribute("j_username", j_username);
}
chain.doFilter(req, res);
}
}

3. Create an orion-web.xml file to specify the ServletFilter as a FORMAUTH filter

<?xml version = '1.0' encoding = 'windows-1252'?>
<orion-web-app>
<web-app>
<filter-mapping>
<filter-name>FormAuthFilter</filter-name>
<dispatcher>FORMAUTH</dispatcher>
</filter-mapping>
</web-app>
<security-role-mapping name="secure" impliesAll="false">
<group name="oc4j-administrators"/>
</security-role-mapping>
</orion-web-app>
3. Access the j_username attribute from the error.jsp page

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<%@ page contentType="text/html;charset=windows-1252"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=windows-1252"/>
<title>Proxy Authentication</title>
<link href="../css/blaf.css" rel="stylesheet" media="screen"/>
</head>
<body>
<h2>Error!</h2>
<%
String username = request.getAttribute("j_username")==null?
"" : (String)request.getAttribute("j_username");
%>
Error logging on as <%=username%>&nbsp;Try again.
<p>
<form method="POST" action="j_security_check">
<input type="text" name="j_username" value="<%=username%>"/>
<input type="password" name="j_password"/>
<input type="submit" value="logon"/>
</form>

</p>
</body>
</html>


Now give it a spin. When an authentication failure occurs, the error.jsp page displays the username that was last provided.


This simple example just demonstrates how to get access to the supplied j_username, what you then decide to do with it is up to you!

No comments: