November 1, 2014
Hot Topics:
RSS RSS feed Download our iPhone app

Securing J2EE Applications with a Servlet Filter

  • February 3, 2005
  • By Michael Klaene
  • Send Email »
  • More Articles »

To accomplish all of this, you need to obtain an instance of AuthorizationManager. Ideally, this object should be a single instance to be used across the application. A common way to obtaining a reference to an object instance in this situation would be through JNDI (Java Naming and Directory Interface). I have chosen to create a singleton instance by using an ApplicationContext from the Spring framework as an alternative to JNDI. An ApplicationContext can be thought of as a central repository for application objects. It requires two things. First, you must create a descriptor file under WEB-INF, by default called application-context.xml, to instruct the ApplicationContext to create a single object that implements AuthorizationManager:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
    "http://www.springframework.org/dtd/spring-beans.dtd">
<!--Application context -->
<beans>
    <!--Our Authorization Bean -->
    <bean id="AuthorizationManager"
          class="examples.AuthorizationManagerDefaultImpl"/>
</beans>

Secondly, you need to add a Spring listener to web.xml to load the ApplicationContext:

<!--Listener that loads Spring's /WEB-INF/applicationContext.xml
    file.-->
<listener>
    <listener-class>org.springframework.web.context.
                    ContextLoaderListener</listener-class>
</listener>

The library spring.jar must also be in your classpath. Please visit the Spring web site for more documentation on this approach. The point is that, in one way or another, AuthorizationFilter needs to reference a single AuthorizationManager instance. A Spring ApplicationContext (and the objects it contains) can be pulled from the current ServletContext by a utility class:

//Obtain AuthorizationManager singleton from Spring ApplicationContext.
ApplicationContext ctx =
    WebApplicationContextUtils.getWebApplicationContext(
        session.getServletContext());
    AuthorizationManager authMgr =
        (AuthorizationManager)ctx.getBean("AuthorizationManager");

You now have everything wired together, so it is time to look at the details of authorization. Note that, by programming to an interface instead of a concrete class, you have concealed the 'how-to' of authorization. This is a good programming practice allowing AuthorizationFilter to ignore the low-level implementation details (and allow you to reimplement things later without impacting the rest of the application). Class AuthorizationManagerDefaultImpl reads role-to-URI mappings from a system properties file called mapping.properties:

# Properties file with role/uri path pattern mappings.
# {Role}={URI path}
ADMIN=/Filters/restricted/admin/*
USER=/Filters/restricted/users/*

This resembles mappings often found in the web.xml file of an application. These mappings could have come from a database or an XML file. In the current implementation, this file will be read and cached in the object's constructor. It will be necessary to restart the container to change these mappings, but this should not be a significant issue:

//Contains role mappings.
private Properties roleMappings;

/**Load mappings from a properties file on the file system.*/
public AuthorizationManagerDefaultImpl() {
     //Read in properties file containing role mappings...
     this.roleMappings = new Properties();
     try {
          this.roleMappings.load(new FileInputStream(
                                 System.getProperty("file.separator")
                                 +"mapping.properties"));
     }
     catch (Exception e) {
           throw new RuntimeException(e);
     }
}

Below is the implementation of isUserAuthorized that iterates through each mapping to see whether it matches the request URI. If it does, you check to see whether the user has the role associated with that mapping. Once found, you can exit.

/**
 *Returns boolean indicating whether user has the appropriate role
 *for the specified URI.
 */
public boolean isUserAuthorized(User user, String uri) {

    boolean matchFound = false;
    boolean authorized = false;

       Iterator i = roleMappings.entrySet().iterator();

       //Loop through each URI mapping and check user's roles.
       //Exit once match is found.
       while( (!authorized)  &&  (i.hasNext()) ) {
           Map.Entry me = (Map.Entry)i.next();

            //Pattern match.  '*' should be interpreted as a wildcard
            //for any ASCII character.
            String mapPattern =
                ((String)me.getValue()).replaceAll("\\*",".*");
            matchFound = Pattern.matches(mapPattern,uri);

            if(matchFound && user.getUserRoles().contains(me.getKey())) {
              authorized = true;
            }
        }
        return authorized;
    }

You iterate through the roleMappings Properties object (Class Properties extends HashTable). Instead of some complex parsing with String methods, I've used Java's pattern matching capabilities. All occurrences of '*' are replaced with '.*' to effectively wildcard your mappings (".*" allows a match on any preceding ASCII character). When found, you check for that role on the User object. It is important to point out the use of cached objects in the example. Nowhere do you hit a database for this information. Because this logic will execute for every matching request, it needs to execute quickly.

Authorization in Action

I have created an index.jsp page to verify AuthorizationFilter is doing its job. Essentially, it provides links to other pages in the application, one public (not in the restricted subfolder), one for users (restricted/users), and one for an admin (restricted/admin). I have also instantiated a User object(that simply gives itself a role in its constructor). This User object has the 'USER' role, but not the 'ADMIN' role. When you attempt to navigate to the public page and the page in the users section, everything works fine. However, when attempting to access the admin page, you are redirected to the error.jsp, which displays the following text: 'User is not authorized to access this area!'.

One overlooked piece of functionality concerning Filters is their ability to handle not only actual requests to the server, but also server-side includes, forwards, and error redirection. In the 2.3 Servlet specification, only the initial request to the server was processed. In the 2.4 specification, however, you can configure a Filter object so that it intercepts other request dispatching actions. This is accomplished with the <dispatcher> element. Currently, AuthorizationFilter could be circumvented if a developer added a jsp forward directive to an admin resource from a user resource. An updated index.jsp tests some forward and include scenarios. Your web.xml file needs to be updated to specify that the Filter should apply to types 'REQUEST', 'FORWARD', 'INCLUDE', and 'ERROR'(though not tested here):

<filter-mapping>
    <filter-name>AuthorizationFilter</filter-name>
    <url-pattern</restricted/*</url-pattern>
    <dispatcher>REQUEST</dispatcher>
    <dispatcher>FORWARD</dispatcher>
    <dispatcher>INCLUDE</dispatcher>
    <dispatcher>ERROR</dispatcher>
</filter-mapping>

The page includeRestricted.jsp attempts to include an admin resource with a single piece of code:

<jsp:include page="restricted/admin/admin.jsp"/>

However, that action will be denied.

Conclusion

In this article, I have demonstrated one way to implement authorization for a J2EE application utilizing a Servlet Filter. This is a simple and unobtrusive way to satisfy authorization requirements. It can be plugged into a new or existing application running on any Servlet container.





Page 2 of 2



Comment and Contribute

 


(Maximum characters: 1200). You have characters left.

 

 


Sitemap | Contact Us

Rocket Fuel