There's a better way. Don't write your own login code!
I'm serious. I've worked with J2EE/JEE since before JSPs were invented, seen who knows how many webapps, including some critical financial apps and even some military stuff. And virtually
every one that employed user-designed login processes had major security loopholes. Not excepting those designed by the shop's Resident Genius and mandated by corporate standards.
J2EE has a security framework built right into the spec and even the minimalist J2EE server implementations provide it. It's known as Container-Managed Security, because the bulk of its functionality is handled by the container, which manages the login (authentication) and authorization of user URL requests. Unauthorized requests are bounced by the container without getting anywhere neat the intended victim application, so you don't have to worry about possible security exploits in application code. In addition to basic authentication, the framework also provides Role-Based Access Control (RBAC) where a user can be assigned one or more security roles and granted or denied access to specific URLs within the webapp. All of which is configured in the web.xml file, which, being declarative, rather than code, is much easier to validate and much harder to mis-code.
You don't need any login logic at all using Container-Managed Security. You simply define the login mechanism (BASIC, via client dialog) or FORM-based (via HTML or
JSP FORM pages), and in the case of FORM-based authentication, define the HTML or JSP template used for the login page (use of more complex technologies, such as JSF for login forms is not recommended).
You then match a security Realm to the webapp when you deploy it. The Realm defines the mechanism and location of the security database that contains the valid userids/passwords and userid/role mappings. It can be LDAP,
JDBC, an XML file (such as the tomcat-users.xml) or just about anything. I''ve written custom Realms that used Web Services, there are single-signon tie-ins to Windows Authentication and so forth.
Once the app is deployed, every incoming URL is scanned against the secured URL
patterns you define in web.xml. If the user requests a secured URL and is not currently authenticated (logged in), then the request is
immediately sidelined and the user is prompted to login. The server will continue to prompt for a valid userid/password until it either receives on or the user gives up and goes away. If the user supplied valid credentials, then the sidelined URL is then resumed automatically as though login had never intervened. Except that now incoming HttpServletRequests will have the user's login ID in the getRemoteUser() method instead of null like an unauthenticated user does.
In the event that an authenticated user attempts to request a URL for which that user isn't assigned an allowed security role, then the server will block the request and display the 503: FORBIDDEN page (you can customize this page in web.xml).
Other than that, it's all magic. You can use a test Realm to avoid having to recreate (or worse, yet to
use) the production security services without changing a single element of the WAR. And this same mechanism works virtually identically for all J2EE webapps regardless of what framework (if any) they employ.
One small caveat for JSF, however. Since the mechanism is based on the URL, and JSF URLs tend to lag behind the resource paths being employed, you need to add a "redirect" to the JSF navigation process for any View which navigates to a secured View. That will cause the URL to track and the proper security context to be applied.