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 ).
The simplest way to get started with container-managed security is to use BASIC authentication, which involves the use of the security realm. The security realm serves as a reference to container-specific security storage. The mechanism for associating this logical realm to the concrete realm varies by container. Typically, realms can be based on flat files (for example, property files or XML files), a relational database, or an LDAP server. Some containers, such as JBoss, provide a mapping between a realm and a Java Authentication and Authorization Service (JAAS) implementation.
For simplicity, the security realm for ABC, Inc. will be implemented using the Tomcat UserDatabaserealm. By its default configuration, Tomcat supports this realm for all applications. The realm retrieves usernames, passwords, roles, and role assignments from the tomcat-users.xml file.
You will add three users to the system to test the functionality—two of the users will be assigned the administrator role. The third user will be defined using a non-administrator role. This third user is defined for testing and illustrative purposes. Add the following four elements (in bold) to the <TOMCAT_HOME>/conf/ tomcat_users.xml file:
Now, deploy this new version of the Mini HR application. You need to restart Tomcat to enable the new functionality. Then, browse to the welcome page, index.jsp. You should see no differences here. Next, click the Add an Employee link. Your browser will display a dialog box similar to that shown in Figure 19-1.
Entering a valid username and password of a user with the administrator role displays the requested page. If the user does not have the administrator role but is otherwise valid, an HTTP status of 403 (Access Denied) is the response. Likewise, if you click Cancel, an HTTP status of 401 (Authentication Required) is returned. If a matching username and password are not found, then the dialog box simply redisplays.
Once a user has been authenticated, the Web application can glean useful user data from the HTTP request. The two methods of interest are getUserPrincipal( ), which can be used to acquire the username, and isUserInRole( ), which can be used to determine if a user has a specified role. These methods can be used in the Action classes to perform such things as the following:
Load the user’s profile and store it in the session
Render a specific response or redirect to a certain URL based on the user’s role.
In addition to the programmatic uses if this data, Struts applications can also use this information to do the following:
Allow role-based access to Actionclasses configured in the struts-config.xmlfile.
Dynamically hide or display presentation components (links, buttons, menus, etc.) based on the user’s role using the <logic:present> and <logic:notPresent> tags.
The first two programmatic uses are available to any JSP/servlet-based application. The Struts-specific uses, however, warrant more discussion.
Action mappings in the struts-config.xml file have an optional rolesattribute, which accepts a comma-separated list of roles. If a user has any one of those roles, the user can access the action. Otherwise, access is denied. This attribute provides a much more natural way of granting/denying access than using the URL patterns. Note, however, that using the action mappings only restricts access to URLs served through the Struts controller (e.g., *.do). It does not restrict access to JSP pages or static HTML pages. The following snippet from the struts-config.xml file shows the use of the rolesattribute:
If your Web application places JSP pages under the WEB-INFfolder and all requests are handled by the Struts controller, the roles attribute can be used to completely control access to Web resources.
As mentioned, the rendering of portions of a JSP page can be based on role using the <logic>tags. Considering the main page, it might be worthwhile to display only the Add an Employee link if the user is an administrator. Such a page might look something like the following:
<logic:present role="administrator"/> <a href="admin_login.jsp">Administrator Login</a> </logic:present> <ul> <li><html:link forward="add">Add an Employee</html:link></li> <li><html:link forward="search">Search for Employees</html:link></li> </ul>
However, this approach leads to a common problem with container-managed security. You want to show the link only if the user is an administrator, but you don't know if the user is an administrator unless the user selects the link and is authenticated. There are several alternatives that you can use to solve this problem. One alternative is to force all users to login--obviously leading to disgruntled users if the login does not add value. Another alternative is to provide a means for a user to proactively log in.
The proactive login behavior can still be accomplished by using container-managed security—albeit through some trickery. What you will do is create a link on the main page (index.jsp) to a security-constrained JSP page (/admin/admin_login.jsp). This page will simply redirect back to the main page. Since it will be a protected page, the user will be forced to log in.
The admin_login JSP page is as simple as the following:
The redirecttag results in an HTTP redirect response. The page attribute of the tag indicates where to redirect the response. In this example, this will result in a redirect to the main page of the application. Since this page is put under the /adminpath, no changes are necessary to the security constraint in the web.xmlfile.
The user experience now is as follows:
When the index page is first displayed, the user does not see the Add an Employee link, but can log in.
Upon login, the index is redisplayed, this time displaying the administrative link.
Upon selecting this link, the user does not have to reauthenticate.