There is a downside to this arrangement, and that is that HTTP BASIC authentication doesn't really support the concept of logging out!
As normal with a Java web application, the logout operation is a matter of invalidating the current HttpSession, throwing away any cached user configuration, and redirecting the user to a 'you are logged out' page. This can be achieved by having a filter or a servlet pick up the 'logout' request, mark the HttpSession associated with the current HttpRequest object, and deleting the JSESSION cookie that identified the session. The response then just needs to contain a logout message, or redirect, or whatever you desire for your site.
That's all well and good if it's just the web application involved. However, in our scenario, we have web server to consider too. As it stands, there's nothing in the HyperText Transfer Protocol ('HTTP' to you and me) that allows the web application to tell the web server that the HttpSession is over - the application doesn't talk to the server, so (according to almost all the web sites I could find with Google) there's no way to tell the server to invalidate it's knowledge of the end user and their credentials.
Or is there....
The key factor is that it is possible to make the server re-challenge the user for their credentials in the same way as they were when they started using the application, and this can be made to have the same effect as logging them out. Note that the critical fact that previous web-pages on this subject seem to have missed is that HTTP BASIC authentication has a realm parameter.
By knowing what the realm is that the web-server used to the authenticate the user, we can cause the browser to re-authenticate against that same realm, by challenging the browser with an HTTP 401 (just like the web-server does).
The process (in my application) works like this:
- The user clicks the logout link to go to /logout.html, which tells the user they are being logged out. This is a nice-to-have page to make this process a little more friendly, you could skip it and go straight to the next step
- The browser pauses for 1 second on this page, then redirects to /logout
- This url is mapped to a logout filter, which does the normal session termination activities I mentioned above. But in order for the next step to work, the filter also registers the current HttpSession key with the LoggedOutServlet (storing it in a singleton HashSet of keys), and creates a has-logged-out cookie with the HttpSession key as the value of the cookie.
- The logoutFilter then redirects to /loggedout which has been mapped to the LoggedOutServlet in my web.xml:
- The LoggedOutServlet looks for the has-logged-out cookie. If cookie exists, and the value is in the servlet's register of logging-out keys, then the key is removed from the register, the cookie marked as deleted, and an Unauthorised (HTTP 401) response is returned to the user:
public void setupResponseForLogout(HttpServletResponse response) { response.setStatus(
HttpServletResponse.SC_ UNAUTHORIZED); // HTTP 401 response.setHeader("WWW- Authenticate", "Basic realm=\"xyzzy\""); } - The user is then prompted by the browser to re-authenticate. The critical thing here is that the realm (set to 'xyzzy' above) is set correctly in the response. If this is done, then the web-server (which must be authenticating with the same realm) will correctly re-authenticate the user if they try to login again. So the consequence is that the user is effectively logged out, and cannot get back into the application without being challenged for their credentials again. This works because the browser has been asked to authenticate against a specific realm, so that will be used in the authentication process, and will force the web server to full re-check the user's credentials - refusing access if they get it wrong.
<servlet> <description>Logout Servlet</description> <display-name>LoggedOutServlet</display-name> <servlet-name>loggedout</servlet-name> <servlet-class> com.xyzzy.web.LoggedOutServlet </servlet-class> </servlet> <servlet-mapping> <servlet-name>loggedout</servlet-name> <url-pattern>/loggedout</url-pattern> </servlet-mapping>