Excluding Pages From Authentication

Java’s Servlet spec allows web applications to delegate authentication and authorization to the servlet container, a mechanism known as container-based security. A lot of people use it for in-house applications or web services because it’s simple and containers like Tomcat already provide several authentication backends to choose from. There’s one common use case though that’s a bit tricky: Secure the entire web application except for a couple of special pages (the login page or the homepage come to mind). Judging from my web searches, many developers struggle with this and eventually implement workarounds. In this article I’ll present a real working solution.

Without further ado, here’s a snippet from a web.xml file that shows how it works:

 
<security-constraint>
  <web-resource-collection>
    <web-resource-name>Private</web-resource-name>
    <description>Matches all pages.</description>
    <url-pattern>/*</url-pattern>
  </web-resource-collection>
  <auth-constraint>
     <role-name>authenticated-user</role-name>
  </auth-constraint>
</security-constraint>
<security-constraint>
  <web-resource-collection>
    <web-resource-name>Public</web-resource-name>
    <description>Matches a few special pages.</description>
    <url-pattern>/index.jsp</url-pattern>
    <url-pattern>/login.jsp</url-pattern>
    <url-pattern>/public/*</url-pattern>
  </web-resource-collection>
  <!-- No auth-constraint means everybody has access! -->
</security-constraint>
<security-role>
  <description>
    A role for all authenticated ("logged in") users. This
    role must be present in the servlet container's user
    management database.
  </description>
  <role-name>authenticated-user</role-name>
</security-role>
<login-config>
  <auth-method>DIGEST</auth-method>
  <realm-name>My Webapp</realm-name>
</login-config>

The example has two security constraints. The first one ("Private") matches all web resources (aka pages) while the second one ("Public") only matches the index page, the login page, and everything below "/public/". Note that the order of the security-constraint elements doesn’t matter! Just like with servlet and filter patterns, the most specific match wins. So a request for "/public/foo" matches "Public" because the pattern "/public/*" is more specific than "/*".

The auth-constraint element specifies which users are allowed access to the matched resource. The role-name given there must refer to a security-role declaration in web.xml and it must also be present in the servlet container’s user database. And finally, we also need a login-config definition to tell the container which method of authentication to use (there are several others that are more or less secure than "DIGEST").

Looking at the "Public" security-constraint again, there’s one non-obvious thing that you only figure out by reading the Servlet spec very thoroughly. The "Public" security-constraint mustn’t specify an auth-constraint element. This means everybody has access, which is exactly what we want.

Searching the web I’ve seen many different attempts that didn’t work:

 
<auth-constraint /> <!-- access for nobody -->

This means that nobody has access! Similarly, the following doesn’t work either:

 
<auth-constraint>
  <role-name>*</role-name> <!-- access only for declared roles -->
</auth-constraint>

The special "*" role matches all declared roles in web.xml. Unfortunately, there’s no magic "anonymous" role for unauthenticated users!

When playing with this example, don’t forget to configure your servlet container accordingly. Here’s an example for Tomcat’s built in user database tomcat-users.xml:

 
<tomcat-users>
  <role rolename="authenticated-user" />
  <user username="matt" password="mypass" 
        roles="authenticated-user" />
</tomcat-users>

It declares a role "authenticated-user" and a user for the role.

Advertisements
This entry was posted in java and tagged , , , . Bookmark the permalink.

16 Responses to Excluding Pages From Authentication

  1. Daniel says:

    This information is very helpful, and hard to come by. Thank you.

  2. Helped me as well, thanks :)

  3. kris says:

    Thanks for this, quick and to the point!

  4. Viz7379 says:

    This is exactly what I am looking for … thanks a lot

  5. Curtis says:

    Great tutorial! Exactly what I was needing for my Form Authentication.

  6. Jörg says:

    Exactly what I needed. Also very useful what you said about what’s NOT working. Thank you!

  7. ghassen says:

    Hello I’have the same problem and i can’t resolve it :

    Logging Area

    Authentication for registered users.

    /*
    GET
    POST

    ADMIN_ALL_INFRA
    IP_PPRD_PARME
    MOE_PROJ_PARME

    Public
    Pour les Applications
    /backend/*
    /processor/*
    /tcihm/*
    GET
    POST

    NONE

    ADMIN_ALL_INFRA

    IP_PPRD_PARME

    MOE_PROJ_PARME

    BASIC
    Please enter your Username

    and i my log i have a http 401 :
    hltcap01-adm – – [19/Aug/2016:14:57:57 +0200] “GET /tcihm/ HTTP/1.1” 401 993
    hltcap01-adm – – [19/Aug/2016:14:58:02 +0200] “GET /tcihm/ HTTP/1.1” 401 993

    Could you tell me please where is the problem ?

    Thks so much !

    • Matthias says:

      A 401 response means that either the client did not provide the required credentials (this typically happens during the initial request) or the presented credentials were invalid. In any case, you managed to enable authentication – you just need to figure out whether your container’s user database is set up correctly.

      • ghassen says:

        Hi when i set my login/pasword evevry things is ok.
        what i want is to set access free for the business applications and for the admin application we need to set login.

  8. ghassen says:

    Logging Area

    Authentication for registered users.

    /*
    GET
    POST

    ADMIN_ALL_INFRA
    IP_PPRD_PARME
    MOE_PROJ_PARME

    Public
    Pour les Applications
    /backend/*
    /processor/*
    /tcihm/*
    GET
    POST

    NONE

    ADMIN_ALL_INFRA

    IP_PPRD_PARME

    MOE_PROJ_PARME

    BASIC
    Please enter your Username

  9. Sandeep says:

    NIce tutorial and very much to the point. Thanks

  10. Gajendra says:

    Hello,
    I want to setup basic authentication for 2 role with different userName and password for different url?

    • Matthias says:

      No problem – you need to configure separate security constraints for each role. Each of these constraints specify the URLs (“web-resource-collection”) and reference different roles in “auth-constraint”.

      • Gajendra says:

        Hello Matthias,
        I did actually. But it return 403 error. For one security-constraint it works fine for /login.

        test
        /testlogin
        GET
        POST

        admin

        BASIC

        midas
        /login
        GET
        POST

        user

        NONE

        BASIC

      • Gajendra says:

        Sorry I tried to copy my web.xml config but it seems something wrong.

      • Gajendra says:

        It was because of cookies. When I refresh cookies it worked but then /login start getting 403. Is there any way to stop browser to remember username and pass?

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s