- Form-Based Authentication
- Example: Form-Based Authentication
- BASIC Authentication
- Example: BASIC Authentication
- Configuring Tomcat to Use SSL
7.2 Example: Form-Based Authentication
In this section I'll work through a small Web site for a fictional company called hot-dot-com.com. I'll start by showing the home page, then list the web.xml file, summarize the various protection mechanisms, show the password file, present the login and login-failure pages, and give the code for each of the protected resources.
The Home Page
Listing 7.7 shows the top-level home page for the Web application. The application is registered with a URL prefix of /hotdotcom so the home page can be accessed with the URL http://host/hotdotcom/index.jsp as shown in Figure 73. If you've forgotten how to assign URL prefixes to Web applications, review Section 4.1 (Registering Web Applications).
Figure 73 Home page for hot-dot-com.com.
Now, the main home page has no security protections and consequently does not absolutely require an entry in web.xml. However, many users expect URLs that list a directory but no file to invoke the default file from that directory. So, I put a welcome-file-list entry in web.xml (see Listing 7.8 in the next section) to ensure that http://host/hotdotcom/ would invoke index.jsp.
Listing 7.7 index.jsp (Top-level home page)
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <HTML> <HEAD> <TITLE>hot-dot-com.com!</TITLE> <LINK REL=STYLESHEET HREF="company-styles.css" TYPE="text/css"> </HEAD> <BODY> <TABLE BORDER=5 ALIGN="CENTER"> <TR><TH CLASS="TITLE">hot-dot-com.com!</TABLE> <P> <H3>Welcome to the ultimate dot-com company!</H3> Please select one of the following: <UL> <LI><A HREF="investing/">Investing</A>. Guaranteed growth for your hard-earned dollars! <LI><A HREF="business/">Business Model</A>. New economy strategy! <LI><A HREF="history/">History</A>. Fascinating company history. </UL> </BODY> </HTML>
The Deployment Descriptor
Listing 7.8 shows the complete deployment descriptor used with the hotdotcom Web application. Recall that the order of the subelements within the web-app element of web.xml is not arbitraryyou must use the standard ordering. For details, see Section 5.2 (The Order of Elements within the Deployment Descriptor).
The hotdotcom deployment descriptor specifies several things:
URLs that give a directory but no filename result in the server first trying to use index.jsp and next trying index.html. If neither file is available, the result is server specific (e.g., a directory listing).
URLs that use the default servlet mapping (i.e., http://host/hotdotcom/ servlet/ServletName) are redirected to the main home page.
Requests to http://host/hotdotcom/ssl/buy-stock.jsp are redirected to https://host/hotdotcom/ssl/buy-stock.jsp. Requests directly to https://host/hotdotcom/ssl/buy-stock.jsp require no redirection. Similarly, requests to http://host/hotdotcom/ssl/FinalizePurchase are redirected to https://host/hotdotcom/ssl/FinalizePurchase. See Section 7.5 for information on setting up Tomcat to use SSL.
URLs in the investing directory can be accessed only by users in the registered-useror administratorroles.
The delete-account.jsp page in the admin directory can be accessed only by users in the administratorrole.
Requests for restricted resources by unauthenticated users are redirected to the login.jsp page in the admin directory. Users who are authenticated successfully get sent to the page they tried to access originally. Users who fail authentication are sent to the login-error.jsp page in the admin directory.
Listing 7.8 WEB-INF/web.xml(Complete version for hot-dot-com.com)
<?xml version="1.0" encoding="ISO-8859-1"?> <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN" "http://java.sun.com/j2ee/dtds/web-app_2_2.dtd"> <web-app> <!-- Give name to FinalizePurchaseServlet. This servlet will later be mapped to the URL /ssl/FinalizePurchase (by means of servlet-mapping and url-pattern). Then, that URL will be designated as one requiring SSL (by means of security-constraint and transport-guarantee). --> <servlet> <servlet-name> FinalizePurchaseServlet </servlet-name> <servlet-class> hotdotcom.FinalizePurchaseServlet </servlet-class> </servlet> <!-- A servlet that redirects users to the home page. --> <servlet> <servlet-name>Redirector</servlet-name> <servlet-class>hotdotcom.RedirectorServlet</servlet-class> </servlet> <!-- Associate previously named servlet with custom URL. --> <servlet-mapping> <servlet-name> FinalizePurchaseServlet </servlet-name> <url-pattern> /ssl/FinalizePurchase </url-pattern> </servlet-mapping> <!-- Turn off invoker. Send requests to index.jsp. --> <servlet-mapping> <servlet-name>Redirector</servlet-name> <url-pattern>/servlet/*</url-pattern> </servlet-mapping> <!-- If URL gives a directory but no filename, try index.jsp first and index.html second. If neither is found, the result is server-specific (e.g., a directory listing). --> <welcome-file-list> <welcome-file>index.jsp</welcome-file> <welcome-file>index.html</welcome-file> </welcome-file-list> <!-- Protect everything within the "investing" directory. --> <security-constraint> <web-resource-collection> <web-resource-name>Investing</web-resource-name> <url-pattern>/investing/*</url-pattern> </web-resource-collection> <auth-constraint> <role-name>registered-user</role-name> <role-name>administrator</role-name> </auth-constraint> </security-constraint> <!-- URLs of the form http://host/webAppPrefix/ssl/blah require SSL and are thus redirected to https://host/webAppPrefix/ssl/blah. --> <security-constraint> <web-resource-collection> <web-resource-name>Purchase</web-resource-name> <url-pattern>/ssl/*</url-pattern> </web-resource-collection> <auth-constraint> <role-name>registered-user</role-name> </auth-constraint> <user-data-constraint> <transport-guarantee>CONFIDENTIAL</transport-guarantee> </user-data-constraint> </security-constraint> <!-- Only users in the administrator role can access the delete-account.jsp page within the admin directory. --> <security-constraint> <web-resource-collection> <web-resource-name>Account Deletion</web-resource-name> <url-pattern>/admin/delete-account.jsp</url-pattern> </web-resource-collection> <auth-constraint> <role-name>administrator</role-name> </auth-constraint> </security-constraint> <!-- Tell the server to use form-based authentication. --> <login-config> <auth-method>FORM</auth-method> <form-login-config> <form-login-page>/admin/login.jsp</form-login-page> <form-error-page>/admin/login-error.jsp</form-error-page> </form-login-config> </login-config> </web-app>
The Password File
With form-based authentication, the server (container) performs a lot of the work for you. That's good. However, shifting so much work to the server means that there is a server-specific component: the assignment of passwords and roles to individual users (see Section 7.1).
Listing 7.9 shows the password file used by Tomcat for this Web application. It defines four users: john (in the registered-user role), jane (also in the registered-user role), juan (in the administrator role), and juana (in the registered-user and administrator roles).
Listing 7.9 install_dir/conf/tomcat-users.xml (First four users)
<?xml version="1.0" encoding="ISO-8859-1"?> <tomcat-users> <user name="john" password="nhoj" roles="registered-user" /> <user name="jane" password="enaj" roles="registered-user" /> <user name="juan" password="nauj" roles="administrator" /> <user name="juana" password="anauj" roles="administrator,registered-user" /> </tomcat-users>
The Login and Login-Failure Pages
This Web application uses form-based authentication. Attempts by not-yet-authenticated users to access any password-protected resource will be sent to the login.jsp page in the admin directory. This page, shown in Listing 7.10, collects the username in a field named j_username and the password in a field named j_password. The results are sent by POST to a resource called j_security_check. Successful login attempts are redirected to the page that was originally requested. Failed attempts are redirected to the login-error.jsp page in the admin directory (Listing 7.11).
Listing 7.10 admin/login.jsp
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <HTML> <HEAD> <TITLE>Log In</TITLE> <LINK REL=STYLESHEET HREF="../company-styles.css" TYPE="text/css"> </HEAD> <BODY> <TABLE BORDER=5 ALIGN="CENTER"> <TR><TH CLASS="TITLE">Log In</TABLE> <P> <H3>Sorry, you must log in before accessing this resource.</H3> <FORM ACTION="j_security_check" METHOD="POST"> <TABLE> <TR><TD>User name: <INPUT TYPE="TEXT" NAME="j_username"> <TR><TD>Password: <INPUT TYPE="PASSWORD" NAME="j_password"> <TR><TH><INPUT TYPE="SUBMIT" VALUE="Log In"> </TABLE> </FORM> </BODY> </HTML>
Listing 7.11 admin/login-error.jsp
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <HTML> <HEAD> <TITLE>Begone!</TITLE> <LINK REL=STYLESHEET HREF="../company-styles.css" TYPE="text/css"> </HEAD> <BODY> <TABLE BORDER=5 ALIGN="CENTER"> <TR><TH CLASS="TITLE">Begone!</TABLE> <H3>Begone, ye unauthorized peon.</H3> </BODY> </HTML>
The investing Directory
The web.xml file for the hotdotcom Web application (Listing 7.8) specifies that all URLs that begin with http://host/hotdotcom/investing/ should be password protected, accessible only to users in the registered-user role. So, the first attempt by any user to access the home page of the investing directory (Listing 7.12) results in the login form shown earlier in Listing 7.10.
Figure 74 Home page for hot-dot-com.com.
shows the initial result, Figure 75 shows the result of an unsuccessful login attempt, and Figure 76 shows the investing home pagethe result of a successful login.Once authenticated, a user can browse other pages and return to a protected page without reauthentication. The system uses some variation of session tracking to remember which users have previously been authenticated.
Listing 7.12 investing/index.html
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <HTML> <HEAD> <TITLE>Investing</TITLE> <LINK REL=STYLESHEET HREF="../company-styles.css" TYPE="text/css"> </HEAD> <BODY> <TABLE BORDER=5 ALIGN="CENTER"> <TR><TH CLASS="TITLE">Investing</TABLE> <H3><I>hot-dot-com.com</I> welcomes the discriminating investor! </H3> Please choose one of the following: <UL> <LI><A HREF="../ssl/buy-stock.jsp">Buy stock</A>. Astronomic growth rates! <LI><A HREF="account-status.jsp">Check account status</A>. See how much you've already earned! </UL> </BODY> </HTML>
Figure 74 Users who are not yet authenticated get redirected to the login page when they attempt to access the investing page.
Figure 75 Failed login attempts result in the login-error.jsp page. Internet Explorer users have to turn off "friendly" HTTP error messages (under Tools, Internet Options, Advanced) to see the real error page instead of a default error page.
Figure 76 Successful login attempts result in redirection back to the originally requested page.
The ssl Directory
The stock purchase page (Listings 7.13 and 7.14) submits data to the purchase finalization servlet (Listing 7.15) which, in turn, dispatches to the confirmation page (Listing 7.16).
Note that the purchase finalization servlet is not really in the ssl directory; it is in WEB-INF/classes/hotdotcom. However, the deployment descriptor (Listing 7.8) uses servlet-mapping to assign a URL that makes the servlet appear (to the client) to be in the ssl directory. This mapping serves two purposes. First, it lets the HTML form of Listing 7.13 use a simple relative URL to refer to the servlet. This is convenient because absolute URLs require modification every time your hostname or URL prefix changes. However, if you use this approach, it is important that both the original form and the servlet it talks to are accessed with SSL. If the original form used a relative URL for the ACTION and was accessed with a normal HTTP connection, the browser would first submit the data by HTTP and then get redirected to HTTPS. Too late: an attacker with access to the network traffic could have obtained the data from the initial HTTP request. On the other hand, if the ACTION of a form is an absolute URL that uses https, it is not necessary for the original form to be accessed with SSL.
Second, using servlet-mapping in this way guarantees that SSL will be used to access the servlet, even if the user tries to bypass the HTML form and access the serv-let URL directly. This guarantee is in effect because the transport-guarantee element (with a value of CONFIDENTIAL) applies to the pattern /ssl/*. Figure 77 through 79 show the results.
Listing 7.13 ssl/buy-stock.jsp
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <HTML> <HEAD> <TITLE>Purchase</TITLE> <LINK REL=STYLESHEET HREF="../company-styles.css" TYPE="text/css"> </HEAD> <BODY> <TABLE BORDER=5 ALIGN="CENTER"> <TR><TH CLASS="TITLE">Purchase</TABLE> <P> <H3><I>hot-dot-com.com</I> congratulates you on a wise investment!</H3> <jsp:useBean id="stock" class="hotdotcom.StockInfo" /> <UL> <LI>Current stock value: <jsp:getProperty name="stock" property="currentValue" /> <LI>Predicted value in one year: <jsp:getProperty name="stock" property="futureValue" /> </UL> <FORM ACTION="FinalizePurchase" METHOD="POST"> <DL> <DT>Number of shares: <DD><INPUT TYPE="RADIO" NAME="numShares" VALUE="1000"> 1000 <DD><INPUT TYPE="RADIO" NAME="numShares" VALUE="10000"> 10000 <DD><INPUT TYPE="RADIO" NAME="numShares" VALUE="100000" CHECKED> 100000 </DL> Full name: <INPUT TYPE="TEXT" NAME="fullName"><BR> Credit card number: <INPUT TYPE="TEXT" NAME="cardNum"><P> <CENTER><INPUT TYPE="SUBMIT" VALUE="Confirm Purchase"></CENTER> </FORM> </BODY> </HTML>
Listing 7.14 (Bean used by buy-stock.jsp)
package hotdotcom; public class StockInfo { public String getCurrentValue() { return("$2.00"); } public String getFutureValue() { return("$200.00"); } }
Listing 7.15 WEB-INF/classes/hotdotcom/FinalizePurchaseServlet.java
package hotdotcom; import java.io.*; import javax.servlet.*; import javax.servlet.http.*; /** Servlet that reads credit card information, * performs a stock purchase, and displays confirmation page. */ public class FinalizePurchaseServlet extends HttpServlet { /** Use doPost for non-SSL access to prevent * credit card number from showing up in URL. */ public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String fullName = request.getParameter("fullName"); String cardNum = request.getParameter("cardNum"); confirmPurchase(fullName, cardNum); String destination = "/investing/sucker.jsp"; RequestDispatcher dispatcher = getServletContext().getRequestDispatcher(destination); dispatcher.forward(request, response); } /** doGet calls doPost. Servlets that are * redirected to through SSL must have doGet. */ public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request, response); } private void confirmPurchase(String fullName, String cardNum) { // Details removed to protect the guilty. } }
Listing 7.16 (Dispatched to from FinalizePurchaseServlet.java)
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <HTML> <HEAD> <TITLE>Thanks!</TITLE> <LINK REL=STYLESHEET HREF="../company-styles.css" TYPE="text/css"> </HEAD> <BODY> <TABLE BORDER=5 ALIGN="CENTER"> <TR><TH CLASS="TITLE">Thanks!</TABLE> <H3><I>hot-dot-com.com</I> thanks you for your purchase.</H3> You'll be thanking yourself soon! </BODY> </HTML>
Figure 77 Warning when user first accesses FinalizePurchaseServlet when Tomcat is using a self-signed certificate. Self-signed certificates result in warnings and are for test purposes only. See Section 7.5 for details on creating them for use with Tomcat and for information on suppressing warnings for future requests.
Figure 78 The stock purchase page must be accessed with SSL. Since the form's ACTION uses a simple relative URL, the initial form submission uses the same protocol as the request for the form itself. If you were concerned about overloading your SSL server (HTTPS connections are much slower than HTTP connections), you could access the form with a non-SSL connection and then supply an absolute URL specifying https for the form's ACTION. This approach, although slightly more efficient, is significantly harder to maintain.
Figure 79 To protect the credit card number in transit, you must use SSL to access the FinalizePurchase servlet. Although FinalizePurchaseServlet dispatches to sucker.jsp, no web.xml entry is needed for that JSP page. Access restrictions apply to the client's URL, not to the behind-the-scenes file locations.
Listing 7.17 investing/account-status.jsp
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <HTML> <HEAD> <TITLE>Account Status</TITLE> <LINK REL=STYLESHEET HREF="../company-styles.css" TYPE="text/css"> </HEAD> <BODY> <TABLE BORDER=5 ALIGN="CENTER"> <TR><TH CLASS="TITLE">Account Status</TABLE> <P> <H3>Your stock is basically worthless now.</H3> But, hey, that makes this a buying opportunity. Why don't you <A HREF="../ssl/buy-stock.jsp">buy some more</A>? </BODY> </HTML>
Figure 710 Selecting the Account Status link on the investing home page does not result in reauthentication, even if the user has accessed other pages since being authenticated. The system uses a variation of session tracking to remember which users have already been authenticated.
The admin Directory
URLs in the admin directory are not uniformly protected as are URLs in the investing directory. I already discussed the login and login-failure pages (Listings 7.10 and 7.11, Figure 74 and 75). This just leaves the Delete Account page (Listing 7.18). This page has been designated as accessible only to users in the administrator role. So, when users that are only in the registered-user role attempt to access the page, they are denied permission (see Figure 711). Note that the permission-denied page of Figure 711 is generated automatically by the server and applies to authenticated users whose roles do not match any of the required onesit is not the same as the login error page that applies to users who cannot be authenticated.
A user in the administrator role can access the page without difficulty (Figure 712).
Listing 7.18 admin/delete-account.jsp
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <HTML> <HEAD> <TITLE>Delete Account</TITLE> <LINK REL=STYLESHEET HREF="../company-styles.css" TYPE="text/css"> </HEAD> <BODY> <TABLE BORDER=5 ALIGN="CENTER"> <TR><TH CLASS="TITLE">Delete Account</TABLE> <P> <FORM ACTION="confirm-deletion.jsp"> Username: <INPUT TYPE="TEXT" NAME="userName"><BR> <CENTER><INPUT TYPE="SUBMIT" VALUE="Confirm Deletion"></CENTER> </FORM> </BODY> </HTML>
Figure 711 When John and Jane attempt to access the Delete Account page, they are denied (even though they are authenticated). That's because they belong to the registered-user role and the web.xml file stipulates that only users in the administrator role should be able to access this page.
Figure 712 Once authenticated, Juan or Juana (in the administrator role) can access the Delete Account page.
The Redirector Servlet
Web applications that have protected servlets should always disable the invoker serv-let so that users cannot bypass security by using http://host/webAppPrefix/servlet/ ServletName when the access restrictions are assigned to a custom servlet URL. In the hotdotcom application, I used the servlet and servlet-mapping elements to register the RedirectorServlet with requests to http://host/hotdotcom/servlet/ anything. This servlet, shown in Listing 7.19, simply redirects all such requests to the application's home page.
Listing 7.19 WEB-INF/classes/hotdotcom/RedirectorServlet.java
package hotdotcom; import java.io.*; import javax.servlet.*; import javax.servlet.http.*; /** Servlet that simply redirects users to the * Web application home page. Registered with the * default servlet URL to prevent access to servlets * through URLs that have no security settings. */ public class RedirectorServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.sendRedirect(request.getContextPath()); } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }
Unprotected Pages
The fact that some pages in a Web application have access restrictions does not imply that all pages in the application need such restrictions. Resources that have no access restrictions need no special handling regarding security. There are two points to keep in mind, however.
First, if you use default pages such as index.jsp or index.html, you should have an explicit welcome-file-list entry in web.xml. Without a welcome-file-list entry, servers are not required to use those files as the default file when a user supplies a URL that gives only a directory. See Section 5.7 (Specifying Welcome Pages) for details on the welcome-file-list element. Second, you should use relative URLs to refer to images or style sheets so that your pages don't need modification if the Web application's URL prefix changes. For more information, see Section 4.5 (Handling Relative URLs in Web Applications).
Listings 7.20 and 7.21 (Figure 713 and 714) give two examples.
Figure 713 The hotdotcom business model.
Figure 714 The distinguished hotdotcom heritage.
Listing 7.20 business/index.html
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <HTML> <HEAD> <TITLE>Business Model</TITLE> <LINK REL=STYLESHEET HREF="../company-styles.css" TYPE="text/css"> </HEAD> <BODY> <TABLE BORDER=5 ALIGN="CENTER"> <TR><TH CLASS="TITLE">Business Model</TABLE> <P> <H3>Who needs a business model?</H3> Hey, this is the new economy. We don't need a real business model, do we? <P> OK, ok, if you insist: <OL> <LI>Start a dot-com. <LI>Have an IPO. <LI>Get a bunch of suckers to work for peanuts plus stock options. <LI>Retire. </OL> Isn't that what many other dot-coms did? </BODY> </HTML>
Listing 7.21 history/index.html
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <HTML> <HEAD> <TITLE>History</TITLE> <LINK REL=STYLESHEET HREF="../company-styles.css" TYPE="text/css"> </HEAD> <BODY> <TABLE BORDER=5 ALIGN="CENTER"> <TR><TH CLASS="TITLE">History</TABLE> <P> <H3>None yet...</H3> </BODY> </HTML>