Skip navigation.

exploreopera

| Help

Sign up | Help

JOracle

Oracle's Java technologies: JDeveloper, OC4J, ADF

Programmatic Authentication for Oracle ADF Business Components

The ADF Business Components make creating and using the model layer extremely fast and easy to manage. They have several very useful features including history columns. History columns are extra auditing fields in an entity object that track changes. The best feature of this is that ADF BC is able to use the session user to populate the created by and modified by columns. However, this only works if you use declarative JAZN authentication (configured in the web.xml deployment descriptor). If you want to programmatically authenticate your users, or you use a single sign-on that passes user information to your application after authentication, the user's details are not accesible in the HTTP request and therefore ADF BC is not able to find the username.

The problem is that when an Application Module is initiated there is no authenticated user in the HTTP request. After the initiation, it doesn't matter whether the user is in the session or not, however it would also be useful to be able to use the getRemoteUser and isUserInRole methods, which will fail unless using the declarative authentication method.

The solution is quite simple. When you have authenticated the user, or retrieved the user information from an SSO implementation, put the username and roles into the session. Then create a new servlet filter for all pages (URL pattern /*), and make sure that it is called before the adfBindings and adfFaces filters, but after any filter that performs or requests authentication.

Sample web.xml
... 
<filter-mapping> 
  <filter-name>SecurityFilter</filter-name> 
  <url-pattern>/*</url-pattern> 
</filter-mapping> 
<filter-mapping> 
  <filter-name>AuthFilter</filter-name> 
  <url-pattern>/*</url-pattern> 
</filter-mapping> 
<filter-mapping> 
  <filter-name>adfBindings</filter-name> 
  <url-pattern>*.jsp</url-pattern> 
</filter-mapping> 
<filter-mapping> 
  <filter-name>adfBindings</filter-name> 
  <url-pattern>*.jspx</url-pattern> 
</filter-mapping> 
<filter-mapping> 
  <filter-name>adfFaces</filter-name> 
  <url-pattern>*.jsp</url-pattern> 
</filter-mapping> 
<filter-mapping> 
  <filter-name>adfFaces</filter-name> 
  <url-pattern>*.jspx</url-pattern> 
</filter-mapping> 
... 

Here I've called mine AuthFilter and the SecurityFilter requests authentication from an SSO provider, receives the username and roles and stores them in the session with the attribute names security.auth.user and security.auth.roles respectively. The AuthFilter looks like this:
import java.io.IOException; 
import java.util.Set; 
import javax.servlet.Filter; 
import javax.servlet.FilterChain; 
import javax.servlet.FilterConfig; 
import javax.servlet.ServletException; 
import javax.servlet.ServletRequest; 
import javax.servlet.ServletResponse; 
import javax.servlet.http.HttpServletRequest; 
import javax.servlet.http.HttpSession; 

public class AuthFilter implements Filter 
{ 
  private static final String SESSION_ATTR_USER =
    "security.auth.user"; 
  private static final String SESSION_ATTR_ROLES =
    "security.auth.roles"; 

  public void doFilter(ServletRequest request, 
    ServletResponse response, FilterChain fc) 
    throws IOException, ServletException 
  { 
    if (ServletRequest instanceof HttpServletRequest) 
    { 
      HttpServletRequest httpRequest = 
        (HttpServletRequest)request; 
      HttpSession session = httpRequest.getSession(); 

      if (session.getAttribute(SESSION_ATTR_USER) != null 
        && session.getAttribute(SESSION_ATTR_ROLES) != null) 
      { 
        String username = 
          (String)session.getAttribute(SESSION_ATTR_USER); 
        Set roleSet = 
          (Set)session.getAttribute(SESSION_ATTR_ROLES); 

        AuthRequestWrapper wrapper = 
          new AuthRequestWrapper(httpRequest, username, roleSet); 
        fc.doFilter(wrapper, response); 
      } 
    } 

    fc.doFilter(request, response); 
  } 

  public void init(FilterConfig filterConfig) 
  { 
  } 

  public void destroy() 
  { 
  } 
}


The main work of the filter is done through the AuthRequestWrapper, which extends javax.servlet.http.HttpServletRequestWrapper and implements the getRemoteUser, isUserInRole and getUserPrincipal methods.
import java.security.Principal; 
import java.util.Set; 
import javax.servlet.http.HttpServletRequest; 
import javax.servlet.http.HttpServletRequestWrapper; 

public class AuthRequestWrapper extends HttpServletRequestWrapper 
{ 
  private String username; 
  private Set roleSet; 
  private Principal principal; 

  public AuthRequestWrapper(HttpServletRequest request, 
    String username, Set roleSet) 
  { 
    super(request); 
    this.username = username; 
    this.roleSet = roleSet; 
    this.principal = new AuthUserPrincipal(username); 
  } 

  public String getRemoteUser() 
  { 
    return username; 
  } 

  public Principal getUserPrincipal() 
  { 
    return principal; 
  } 

  public boolean isUserInRole(String roleName) 
  { 
    return roleSet.contains(roleName); 
  } 
}


AuthUserPrincipal is a small class that implements java.security.Principal to pass the username to ADF BC.
public class AuthUserPrincipal implements java.security.Principal 
{ 
  private String username; 

  public AuthUserPrincipal(String username) 
  { 
    this.username = username; 
  } 

  public String getName() 
  { 
    return username; 
  } 
}


And that’s it. ADF BC finds the username in the request wrapper and initiates an Application Module instance with it, the history columns get populated correctly, and roles are accessible in the request for authorisation.

If, like me, you need to pass roles from the SSO to your application, I will write another post about how to modify CAS (Central Authentication Service) to do this.

Edit 5th December 2006:
Due to a request by Rashid (see comments), I'm including the SecurityFilter in this post.

My SecurityFilter class is loosely based on the CASFilter class that comes with the CAS Java client. If you take out which authentication method it uses, the part that allows AuthFilter to work is the same.

Here I've highlighted the parts of the code that are specific to my AuthFilter implementation.

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

public class SecurityFilter implements Filter
{ 
/* START - AuthFilter specific */
  private static final String SESSION_ATTR_USER =
    "security.auth.user"; 
  private static final String SESSION_ATTR_ROLES =
    "security.auth.roles"; 
/* END - Auth Filter specific */
  private static final String SESSION_ATTR_RECEIPT =
    "security.auth.receipt";

  private String loginUrl = null;
  private String validateUrl = null;

  public void init(FilterConfig filterConfig)
    throws ServletException
  {
    loginUrl = filterConfig.getInitParameter("security.auth.loginUrl");
    validateUrl = filterConfig.getInitParameter("security.auth.validateUrl");
          
    if (validateUrl == null) throw new ServletException("validateUrl parameter must be set.");
    if (loginUrl == null) throw new ServletException("loginUrl parameter must be set.");
  }

  public void destroy()
  {
  }
  
  public void doFilter(ServletRequest request, ServletResponse response, FilterChain fc)
    throws IOException, ServletException
  {
    if (!(request instanceof HttpServletRequest) || !(response instanceof HttpServletResponse))
    {
      throw new ServletException("SecurityFilter only accepts HTTP requests");
    }
    
    HttpServletRequest httpRequest = (HttpServletRequest)request;    
    HttpServletResponse httpResponse = (HttpServletResponse)response;
    HttpSession session = httpRequest.getSession();
    
/* START - CAS specific */
    AuthReceipt receipt = (AuthReceipt)session.getAttribute(Constants.SESSION_ATTR_RECEIPT);
    if (receipt != null)
    {
      fc.doFilter(request, response);
      return;
    }
    
    String ticket = request.getParameter("ticket");
    if (ticket == null || ticket.equals(""))
    {
      redirectToSSO(httpRequest, httpResponse);
      return;
    }
    
    try
    {
      receipt = getAuthReceipt(httpRequest);
    }
    catch (AuthenticationException e)
    {
      throw new ServletException(e);
    }
/* END - CAS specific
    
    if (session != null)
    {
/* START - AuthFilter specific */
      session.setAttribute(SESSION_ATTR_USER, receipt.getUsername());
      session.setAttribute(SESSION_ATTR_ROLES, receipt.getAcl());
/* END - AuthFilter specific */
      session.setAttribute(SESSION_ATTR_RECEIPT, receipt);
    }
    
    fc.doFilter(request, response);
  }
  
/* REST - CAS specific */
  private AuthReceipt getAuthReceipt(HttpServletRequest request)
    throws ServletException, AuthenticationException
  {
    TicketValidator tv = null;
    tv = new TicketValidator();
    tv.setValidateUrl(validateUrl);
    tv.setServiceTicket(request.getParameter("ticket"));
    tv.setService(getService(request));
    return AuthReceipt.getReceipt(tv);
  }
  
  private void redirectToSSO(HttpServletRequest request, HttpServletResponse response)
    throws IOException, ServletException
  {
    String ssoLoginUrl = loginUrl + "?service=" + getService(request);
    response.sendRedirect(ssoLoginUrl);
  }
  
  private String getService(HttpServletRequest request)
  {
    String serverName = request.getLocalName() + ":" + request.getLocalPort();
    String service = "http://" + serverName + request.getRequestURI();
    
    if (request.getQueryString() != null)
    {
      String queryString = request.getQueryString();
      int ticketIndex = queryString.indexOf("&ticket=");
      
      if (ticketIndex == -1)
      {
        service += "?" + queryString;
      }
      else
      {
        service += "?" + queryString.substring(0, ticketIndex);
      }
    }
    return service;
  }
}


The main bit is where you add the session variables, the rest is how you get those values in the first place, which depends on your SSO or custom authentication implementation.

Oracle ADF: Adding/deleting entries in a many-to-many relationshipInstalling Oracle MetaLink Patches Without Perl

Comments

avatar
Excellent work. I am trying to implement the dynamic JDBC credentials with ADF. Actually, I implemented my whole application with ADF using JAAS security. Later on, I had to write code for dynamic JDBC credentials to implement history details. I implemented my solution based on the guidelines provided in this article on oracle website. http://www.oracle.com/technology/products/jdev/howtos/10g/dynamicjdbchowto.html Now, my problem is that I could not get the logged in username in ADF environment. I wanted to add your solution into my project to see if that helps. I will appreciate if you could add here the code for the "Security filter". Also, please tell me where to add the AuthUserPrincipal class in my application and how can I call that class in ADF to get username. Thank you.

By RashidBhatti, # 1. December 2006, 16:12:53

avatar
Hi Rashid,

Thanks for your comment.

I've updated the post with the SecurityFilter - I hope it helps. As for the AuthUserPrincipal, I simply put it in the same package as the filters (which for me was myapp.controller.security.client).

To get the username, you call the getRemoteUser() method of the HTTP request.

Example JSP

<html>
<head>
<title>Example</title>
</head>
<body>
<h1><%=request.getRemoteUser()%></h1>
</body>
</html>


That will work in a JSP and in a servlet. If you need to access it in other places, could you tell me where and I can find out if it's possible?

Regards,
DominionSpy

By dominionspy, # 5. December 2006, 15:40:22

avatar
Michael Fons writes:

Mathew,

I am not sure if you article applies to my issue now or not. It seems to kind of fit, and kind of not.

We have written an ADF BC/JSF app using JDev 10.1.3.3.0. We are using SSO to authenticate our application.

Here is what I am observing:
1. Each time a user logs into this application, request.getRemoteUser() is populated and available (I get to this using the Faces Context current instance, External Context).
2. At the application module level, AM.getUserPrincipalName() is available only the first time the end user logs in. If they close their browser, and log in again, getUserPrincipalName() returns null.

Can anybody think why this would be the case?

As a partial solution, I have created an attribute in the app module impl which I write to as soon as a UserInfo session managed bean starts up.

But I need access to the username in AM.prepareSession() because we are setting our user context using VPD.

So I have a problem, you see?

By anonymous user, # 17. June 2008, 21:40:51

avatar
JP writes:

We are facing the same problem in getting logged in user id from prepareSession() for VPD. Did you find any solution to this problem?

By anonymous user, # 17. September 2008, 20:21:19

Write a comment

Comment
(BBcode and HTML is turned off for anonymous user comments.)

Please type this security code : 59c17f

Smilies