Home arrow Java arrow Page 6 - Securing Struts Applications
JAVA

Securing Struts Applications


Web applications often require that certain parts of the system be secured. Some security requirements can be satisfied with standard security mechanisms, while others call for a customized approach. Sometimes, as a developer, you may need to use a combination of these two techniques. This article helps you examine the issues and techniques. It is excerpted from chapter 19 of the book The Complete Reference: Struts, written by James Holmes (McGraw-Hill/Osborne, 2004; ISBN: 0072231319 ).

Author Info:
By: McGraw-Hill/Osborne
Rating: 5 stars5 stars5 stars5 stars5 stars / 111
September 15, 2005
TABLE OF CONTENTS:
  1. · Securing Struts Applications
  2. · Using Container-Managed Security
  3. · BASIC Login
  4. · FORM-Based Login
  5. · Application-Managed Security
  6. · Page/Action-Level Security Checks
  7. · Using Cookies
  8. · SSLEXT to the Rescue

print this article
SEARCH DEVARTICLES

Securing Struts Applications - Page/Action-Level Security Checks
(Page 6 of 8 )

You want to disallow access to the JSP page and, more importantly, the AddAction (add.do). The JSP page can be checked by adding in JSP logic, similar to the index page, that looks for the User object in the session and checks whether the user is an administrator. If the user is not an administrator, a redirect tag can be used to send the user back to the index page. To protect the action, the most obvious solution is to add these same checks into the AddAction class. If the checks fail, the user can be redirected to a page where an error could be displayed, or an exception can be thrown, or an appropriate HTTP status (e.g., 400) can be set in the response. While this solution works for one JSP and one action, it will not scale when many pages and actions are involved.

One mechanism for making this solution scale for the action is to place the authorization check in a base Action class. If, however, you still want to use role-based access for authorization, a better solution is to extend the Struts request processor.

Extending Struts’ Request Processing

Struts’ request processing can be customized by extending RequestProcessor. Specifically, security customizations can be brought into play in particular methods of RequestProcessor. The processRoles( ) method determines how roles, specified for an action mapping via the roles attribute, are handled. Its purpose is to ensure that a user who is accessing an action with assigned roles has at least one of those roles. The method returns true to continue processing normally or false to stop processing and return an appropriate response. The default implementation uses the HttpServletRequest.isUserInRole( ) method to determine if a user has a particular role. This can be used with container-managed security. For application-managed security, you need to override this method in a custom RequestProcessor. For your implementation, you will get the User object from the session and then call the User.hasRole( ) method. The custom request processor with the overridden method is shown here:

package com.jamesholmes.minihr; 
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.*;
import org.apache.struts.action.*;
import com.jamesholmes.minihr.security.User;
public class CustomRequestProcessor extends RequestProcessor {
 
protected boolean processRoles(HttpServletRequest request,
    HttpServletResponse response, ActionMapping mapping)
    throws IOException, ServletException
  {
    // Is this action protected by role requirements?
    String roles[] = mapping.getRoleNames();
    if ((roles == null) || (roles.length < 1)) {
     
return (true);
    }
    // Check the current user against the list of required roles
    HttpSession session = request.getSession();
    User user = (User) session.getAttribute("user");
    if (user == null) {
     
return false;
    }
    for (int i = 0; i < roles.length; i++) {
     
if (user.hasRole(roles[i])) {
        return (true);
     
}
    }
    response.sendError(HttpServletResponse.SC_BAD_REQUEST,
                      
getInternal().getMessage("notAuthorized",
                       mapping.getPath()));
    return (false);
  }
}

Now, you can add the rolesattribute back to your action mapping and the action will be protected. Of course, you also need to plug the customized request processor into the Struts configuration file. That still leaves the somewhat nagging problem of unauthorized users gaining access to the JSP pages. One solution is to place all the JSP pages under WEB-INF and route all JSP page requests through the ActionServlet by using ForwardActions. This is an appealing solution, because you can then use the roles attribute on these actions. This does mean a fairly drastic change to the organization of a Web application and may require substantial modification of your JSP pages.

However, as you will see in the following section, servlet filters can be used to implement security policies that can be applied to related Web resources.

In addition to the processRoles( ) method, there is a more generic processPreprocess( ) method. That method should return true to continue standard processing or false to indicate that the response is complete. That method is a good place to put security checks that cannot be implemented using roles alone.

Using Servlet Filters for Security

Servlet filters were introduced as part of the Servlet 2.3 specification. They provide a powerful mechanism for creating customized request and response processing that can be applied across many Web resources. You can create a filter and then map it to a collection of URLs by using the URL mapping discussed earlier in the chapter. Filters can alter a request before it arrives at its destination and, likewise, can modify the response after it leaves a destination. Filters can be applied to static HTML pages, JSP pages, Struts actions—essentially, any resource that you can specify with a URL.

You can use a filter to implement role-based access controls. The filter in effect performs the same checks that were implemented in the custom RequestProcessor. The filter determines if a user is allowed access to a given Web resource. It checks if the user has been authenticated and if the user has one of the required roles. If either of these checks fails, the filter stores an appropriate error message in the request and forwards the request to a URL. Initialization parameters are used to specify the authorization as well as the page to forward to if an error occurs. As you can see, the initialization parameters enable the creation of filter classes that can easily be reused. Here is the complete implementation for the authorization filter that Mini HR will be using:

package com.jamesholmes.minihr.security; 
import java.io.IOException;
import javax.servlet.*;
import javax.servlet.http.*;
import org.apache.struts.Globals;
import org.apache.struts.action.*;
public class AuthorizationFilter implements Filter {
  private String[] roleNames;
  private String onErrorUrl;
 
public void init(FilterConfig filterConfig)
     
throws ServletException {
    String roles = filterConfig.getInitParameter("roles");
    if (roles == null || "".equals(roles)) {
     
roleNames = new String[0];
    }
    else {
     
roles.trim();
      roleNames = roles.split(
\\s*,\\s*);
    }
    onErrorUrl = filterConfig.getInitParameter("onError");
    if (onErrorUrl == null || "".equals(onErrorUrl)) {
     
onErrorUrl = "/index.jsp";
    }
  }
  public void doFilter(ServletRequest request,  
                       ServletResponse response,
                       FilterChain chain)
                
throws IOException, ServletException {
    HttpServletRequest req = (HttpServletRequest) request;
    HttpServletResponse res = (HttpServletResponse) response;
   
HttpSession session = req.getSession();
    User user = (User) session.getAttribute("user");
    ActionErrors errors = new ActionErrors();
    if (user == null) {
     
errors.add(ActionErrors.GLOBAL_ERROR,
       
new ActionError("error.authentication.required"));
    }
    else {
     
boolean hasRole = false;
      for (int i=0; i<roleNames.length; i++) {
       
if (user.hasRole(roleNames[i])) {
          hasRole = true;
          break;
        }
      }
      if (!hasRole) {
       
errors.add(ActionErrors.GLOBAL_ERROR,
          new ActionError("error.authorization.required"));
     
}
    }
    if (errors.isEmpty()) {
     
chain.doFilter(request, response);
    }
    else {
     
req.setAttribute(Globals.ERROR_KEY, errors);
      req.getRequestDispatcher(onErrorUrl).forward(req, res);
    }
  }
 
public void destroy() {
  }
}

First, notice that the Authorization Filterclass implements Filter. Thus, it must implement the init( ), doFilter( ), and destroy( ) methods. The comma-separated list of roles and the URL of the error page to forward to are retrieved from the initialization parameters in the init( ) method. The doFilter( ) method first checks if there is a User in the session. If not, an appropriate ActionError is created and no further checks are performed. Otherwise, it iterates through the list of roles to determine if the user has any of them. If not, an ActionError is created. If any errors were created, then a RequestDispatcher is created to forward to the given URL; otherwise, the doFilter( ) method calls chain.doFilter( ) to continue normal processing. This implementation also demonstrates the ability to integrate filters with Struts. You could have provided a more generic implementation, by calling the sendError( ) method of the HttpServletResponse class with an appropriate HTTP status code (e.g., 401).

Filters are configured and deployed like servlets. In the web.xml file, you can specify the filter name and class, and initialization parameters. Then, associate the filter with a URL pattern in a filter mapping. The following is the snippet from the web.xml file that shows the necessary changes for this filter:

<filter>
  <filter-name>adminAccessFilter</filter-name>
  <filter-class>
 
   
com.jamesholmes.minihr.security.AuthorizationFilter 
  </filter-class>
  <init-param>
   
<param-name>roles</param-name>
   
<param-value>administrator</param-value>
  </init-param>
  <init-param>
   
<param-name>onError</param-name>
    <param-value>/index.jsp</param-value>
  </init-param>
</filter>
<filter-mapping>
  <filter-name>adminAccessFilter</filter-name>
  <url-pattern>/admin/*</url-pattern>
</filter-mapping>

As you can see, the Authorization Filteris fairly generic. There is, in fact, an open-source security filter known as SecurityFilter that is worth considering.This filter permits implementation of a custom security policy, yet still allows programmatic access to role and user information (i.e., the request.isUserInRole( ) and request.getUserPrincipal( ) methods) that is generally only available when using container-managed authentication. It performs this magic by using wrapper classes around the HttpServletRequest. SecurityFilter is configured using a separate configuration file that is very similar to the standard security-constraint element of the web.xml file. It provides the benefits of application-managed security while still enabling standard role processing without modification. There is no need to extend the Struts RequestProcessor for role handling, and the <logic:equal> tag with the role attribute will also work correctly. SecurityFilter is implemented similarly to AuthorizationFilter. You will want to understand how the filter works and its limitations before migrating your application.

Like servlets and Struts actions, filters are extremely powerful. You have complete access to the Java API that can be applied as needed using URL patterns. However, to truly integrate filters with Struts, you may need to delve into Struts implementation details as was shown in the AuthorizationFilter.


blog comments powered by Disqus
JAVA ARTICLES

- Java Too Insecure, Says Microsoft Researcher
- Google Beats Oracle in Java Ruling
- Deploying Multiple Java Applets as One
- Deploying Java Applets
- Understanding Deployment Frameworks
- Database Programming in Java Using JDBC
- Extension Interfaces and SAX
- Entities, Handlers and SAX
- Advanced SAX
- Conversions and Java Print Streams
- Formatters and Java Print Streams
- Java Print Streams
- Wildcards, Arrays, and Generics in Java
- Wildcards and Generic Methods in Java
- Finishing the Project: Java Web Development ...

Watch our Tech Videos 
Dev Articles Forums 
 RSS  Articles
 RSS  Forums
 RSS  All Feeds
Write For Us 
Weekly Newsletter
 
Developer Updates  
Free Website Content 
Contact Us 
Site Map 
Privacy Policy 
Support 

Developer Shed Affiliates

 




© 2003-2017 by Developer Shed. All rights reserved. DS Cluster - Follow our Sitemap
Popular Web Development Topics
All Web Development Tutorials