Search code examples
apachetomcatjsf-2basic-authentication

Logout from web app using tomcat Basic authentication


I am using tomcat basic authentication for my web app:

I added following lines to web.xml in my web app:

<security-constraint>
    <web-resource-collection>
        <web-resource-name>webpages</web-resource-name>
            <url-pattern>/*</url-pattern>
    </web-resource-collection>
    <auth-constraint>
        <role-name>*</role-name>
    </auth-constraint>

    <user-data-constraint>
        <!-- transport-guarantee can be CONFIDENTIAL, INTEGRAL, or NONE -->
        <transport-guarantee>NONE</transport-guarantee>
    </user-data-constraint>
</security-constraint>

<login-config>
    <auth-method>BASIC</auth-method>
</login-config>
<security-role>
    <role-name>*</role-name>
</security-role>

My logout link:

<h:commandLink value="Logout" action="#{userBean.logout}" />

My logout link action:

public void logout() throws IOException
{
    FacesContext.getCurrentInstance().getExternalContext().invalidateSession();
    FacesContext.getCurrentInstance().getExternalContext().redirect("add_international_job.faces");
}

Now when logout is called, it redirects to another page which should require authentication. But it is rendered as the user is logged in. PS: when the user first time types the url of the same page in address bar, he is presented with authentication challenge(it means that there is no problem in making that page password protected).


Solution

  • You're using HTTP BASIC authentication instead of HTTP FORM authentication with j_security_check. The BASIC authentication is done by Authorization request header from the browser side, which is session independent.

    To force a "logout" on BASIC authentication, the server basically needs to return a 401 response.

    FacesContext facesContext = FacesContext.getCurrentInstance();
    ExternalContext externalContext = facesContext.getExternalContext();
    externalContext.invalidateSession();
    externalContext.responseSendError(401, "You are logged out.");
    facesContext.responseComplete();
    

    This will present a HTTP 401 error page which is customizable as <error-page> in web.xml.

    You can instead also return a HTML page with meta refresh so that the enduser is redirected to the desired target page as specified in the meta refresh header content.

    FacesContext facesContext = FacesContext.getCurrentInstance();
    ExternalContext externalContext = facesContext.getExternalContext();
    externalContext.invalidateSession();
    externalContext.setResponseStatus(401);
    externalContext.getResponseOutputWriter().write("<html><head><meta http-equiv='refresh' content='0;add_international_job.faces'></head></html>");
    facesContext.responseComplete();
    

    This seems indeed pretty low level and hacky, but the BASIC authentication is also pretty low level. This isn't necessary when using FORM authentication. Just invalidating the session and sending a normal redirect should work for FORM authentication.