In another tutorial, we set up TomcatSW with SSL with a self-signed certificate. Since we set this up, this tutorial will use SSL, since it is concerned with authentication, which typically should use encrypted communication.
Basic authentication's pop-up window is fine but not exactly pretty. Form authentication allows you to specify an htmlW file (or jspW) to go for authentication. You can also specify a file to go to if authentication fails. The demonstration project is called 'tomcat-demo'. It has a web.xmlW file, a login.html file, a login-failed.html file, and a TestServlet class.

The web.xml file is shown below. Notice that login-config → auth-method specifies FORM since we want to use Form authentication. Note that form-login-page points to our login.html file in our web directory, and form-error-page points to our login-failed.html file. Of further interest is the transport-guarantee value, which is CONFIDENTIAL. This causes a request for a protected resource on a non-secure port to be redirected to a secure port. This allows us to authenticate securely with HTTPS even if the initial request is to the HTTP port.
web.xml
<?xml version="1.0" encoding="UTF-8"?> <web-app id="tomcat-demo" version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"> <servlet> <servlet-name>TestServlet</servlet-name> <servlet-class>test.TestServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>TestServlet</servlet-name> <url-pattern>/test</url-pattern> </servlet-mapping> <security-constraint> <web-resource-collection> <web-resource-name>Wildcard means whole app requires authentication</web-resource-name> <url-pattern>/*</url-pattern> <http-method>GET</http-method> <http-method>POST</http-method> </web-resource-collection> <auth-constraint> <role-name>tomcat</role-name> </auth-constraint> <user-data-constraint> <!-- transport-guarantee can be CONFIDENTIAL, INTEGRAL, or NONE --> <transport-guarantee>CONFIDENTIAL</transport-guarantee> </user-data-constraint> </security-constraint> <login-config> <auth-method>FORM</auth-method> <form-login-config> <form-login-page>/login.html</form-login-page> <form-error-page>/login-failed.html</form-error-page> </form-login-config> </login-config> </web-app>
The login.html file contains a form with j_security_check as its action. It contains a text input called j_username and a password input called j_password. These are the input names that need to be used in order for Tomcat to process the Form authentication.
login.html
<form method="POST" action="j_security_check"> <table> <tr> <td colspan="2">Login to the Tomcat-Demo application:</td> </tr> <tr> <td>Name:</td> <td><input type="text" name="j_username" /></td> </tr> <tr> <td>Password:</td> <td><input type="password" name="j_password"/ ></td> </tr> <tr> <td colspan="2"><input type="submit" value="Go" /></td> </tr> </table> </form>
The login-failed.html file displays a simple message if the Form authentication fails.
login-failed.html
<p> Sorry, login failed! </p>
The TestServlet class is shown below. It enumerates over the request headers from the browser and writes them to the response.
TestServlet.java
package test; import java.io.IOException; import java.io.PrintWriter; import java.util.Enumeration; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class TestServlet extends HttpServlet { private static final long serialVersionUID = 1L; protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { response.setContentType("text/html"); PrintWriter out = response.getWriter(); out.println("This is the Test Servlet"); Enumeration headerNames = request.getHeaderNames(); while (headerNames.hasMoreElements()) { String headerName = (String) headerNames.nextElement(); out.print("<br/>Header Name: <em>" + headerName); String headerValue = request.getHeader(headerName); out.print("</em>, Header Value: <em>" + headerValue); out.println("</em>"); } } }
In another tutorial, I changed the Tomcat HTTPS connector port to 4321 rather than the default 8443. I also changed the HTTP connector port's redirectPort to 4321 so that the request to 8080 will get redirected to port 4321 (to handle the transport-guarantee CONFIDENTIAL setting mentioned earlier).
<Connector port="8080" maxHttpHeaderSize="8192" maxThreads="150" minSpareThreads="25" maxSpareThreads="75" enableLookups="false" redirectPort="4321" acceptCount="100" connectionTimeout="20000" disableUploadTimeout="true" /> <Connector port="4321" maxHttpHeaderSize="8192" maxThreads="150" minSpareThreads="25" maxSpareThreads="75" enableLookups="false" disableUploadTimeout="true" acceptCount="100" scheme="https" secure="true" clientAuth="false" sslProtocol="TLS" />
(Continued from page 1)
Let's start our demo project in EclipseSW and try hitting our test servletW. Since our certificate is self-signed, we get a warning message from IE7 before the browser gets redirected to the SSL port. I clicked 'Continue to this website...'
We are greeted by our login.html file. Notice that we have been redirected to the secure port, 4321, and that the protocol is HTTPS. To test out the login-failed.html page, I'll enter an invalid username.
If authentication fails, we're greeted by the login-failed.html page.
Let's try again to hit our test servlet, and this time, let's enter a valid username and password that has the correct role.
This time, authentication passes and we are able to hit the test servlet, which displays the request headers that the servlet read from the client request. If you look carefully, you'll notice that the 'authentication' header that was present for Basic authentication is gone.
If we refresh the page, you'll notice that a 'cookie' header value is present, and it contains a JSESSIONID value, indicating that Form-based authentication uses sessions for its authentication mechanism.
![]()
(Continued from page 2)
Just for fun, let's delve in a little deeper by monitoring the TCP/IP communication between the browser and TomcatSW. I changed the transport-guarantee to NONE so that I could use HTTP rather than HTTPS to watch the communication between the browser and Tomcat. I set up a TCP/IP Monitor on port 8081 to forward to 8080.
The browser makes the following request to our server, asking for tomcat-demo's 'test', which is our test servletW.
GET /tomcat-demo/test HTTP/1.1 Accept: */* Accept-Language: en-us UA-CPU: x86 Accept-Encoding: gzip, deflate User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; .NET CLR 1.0.3705; .NET CLR 1.1.4322; Media Center PC 4.0; .NET CLR 2.0.50727) Host: localhost:8081 Connection: Keep-AliveThe server sends back the following response to the browser. Notice that the header contains a Set-Cookie directive with a JSESSIONID value. This JSESSIONID string is an identifier that the browser can later send back to the server, thus letting the server identify the particular browser client and its 'session'. This response contains the login.html form.
HTTP/1.1 200 OK Server: Apache-Coyote/1.1 Pragma: No-cache Cache-Control: no-cache Expires: Wed, 31 Dec 1969 16:00:00 PST Set-Cookie: JSESSIONID=318A0B6C6E5C15E2D2AA2ACF9D531AC8; Path=/tomcat-demo ETag: W/"407-1177060024390" Last-Modified: Fri, 20 Apr 2007 09:07:04 GMT Content-Type: text/html Content-Length: 407 Date: Sun, 22 Apr 2007 09:35:31 GMT <form method="POST" action="j_security_check"> <table> <tr> <td colspan="2">Login to the Tomcat-Demo application:</td> </tr> <tr> <td>Name:</td> <td><input type="text" name="j_username" /></td> </tr> <tr> <td>Password:</td> <td><input type="password" name="j_password"/ ></td> </tr> <tr> <td colspan="2"><input type="submit" value="Go" /></td> </tr> </table> </form>The user fills out the form with the name 'myname' and the password 'mypassword' and clicks the submit button on the form, which sends the following request to the server to /tomcat-demo/j_security_check. The name and password get POSTed to Tomcat, and we can see the form values in the request body. Notice that the browser sends the JSESSIONID cookieW value back to the server, thus letting the server know what client is trying to talk to it.
POST /tomcat-demo/j_security_check HTTP/1.1 Accept: */* Referer: http://localhost:8081/tomcat-demo/test Accept-Language: en-us Content-Type: application/x-www-form-urlencoded UA-CPU: x86 Accept-Encoding: gzip, deflate User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; .NET CLR 1.0.3705; .NET CLR 1.1.4322; Media Center PC 4.0; .NET CLR 2.0.50727) Host: localhost:8081 Content-Length: 39 Connection: Keep-Alive Cache-Control: no-cache Cookie: JSESSIONID=318A0B6C6E5C15E2D2AA2ACF9D531AC8 j_username=myname&j_password=mypasswordSince the authentication succeeds, the server now gives access to the resource to the client. It sends a 302 message to the client, directing the browser to make a new request to the test servlet URL.
HTTP/1.1 302 Moved Temporarily Server: Apache-Coyote/1.1 Location: http://localhost:8081/tomcat-demo/test Content-Length: 0 Date: Sun, 22 Apr 2007 09:35:37 GMTThe client makes a new request for the test servlet URL (/tomcat-demo/test) and includes the JSESSIONID cookie so that the server can identify that this is the correct client to give access to the resource.
GET /tomcat-demo/test HTTP/1.1 Accept: */* Referer: http://localhost:8081/tomcat-demo/test Accept-Language: en-us UA-CPU: x86 Accept-Encoding: gzip, deflate User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; .NET CLR 1.0.3705; .NET CLR 1.1.4322; Media Center PC 4.0; .NET CLR 2.0.50727) Host: localhost:8081 Connection: Keep-Alive Cache-Control: no-cache Cookie: JSESSIONID=318A0B6C6E5C15E2D2AA2ACF9D531AC8The server returns the response generated by the test servlet, which we can see below.
HTTP/1.1 200 OK Server: Apache-Coyote/1.1 Pragma: No-cache Cache-Control: no-cache Expires: Wed, 31 Dec 1969 16:00:00 PST Content-Type: text/html;charset=ISO-8859-1 Content-Length: 682 Date: Sun, 22 Apr 2007 09:35:37 GMT This is the Test Servlet <br/>Header Name: <em>accept-encoding</em>, Header Value: <em>gzip, deflate</em> <br/>Header Name: <em>connection</em>, Header Value: <em>Keep-Alive</em> <br/>Header Name: <em>host</em>, Header Value: <em>localhost:8081</em> <br/>Header Name: <em>accept-language</em>, Header Value: <em>en-us</em> <br/>Header Name: <em>user-agent</em>, Header Value: <em>Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; .NET CLR 1.0.3705; .NET CLR 1.1.4322; Media Center PC 4.0; .NET CLR 2.0.50727)</em> <br/>Header Name: <em>ua-cpu</em>, Header Value: <em>x86</em> <br/>Header Name: <em>accept</em>, Header Value: <em>*/*</em>Hopefully this tutorial has helped you understand how to set up Form authentication in a web application using Tomcat. I hope that it has also helped shed light on some of the mechanics behind how Form authentication works, since we looked at the detailed communication between the browser and the server.