Search code examples
javaservletsfilterlicensingweb.xml

Java : Licensing a webapp. Check for license before login happens


I want to make my webapp license protected. When any page/resource of webapp is requested, I want to first check for the license. If license is not found then I want to redirect to license upload page.

I have created a filter which maps to all the requests where I can check for license and redirect if necessary. The problem is that my webapp has security constraint of login authentication. see web.xml at the end for more information.

Because of the security constraint, all the requests are first intercepted by login authentication and then forwarded to my filter. However, I want to check for license before the login can happen.

Here is a related question I asked.

Java : Intercept all requests before they go to login authentication

Prioritizing filter over the security constraint seems to be impossible. So, I want to ask is there any other way I can approach this use case?

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
    id="WebApp_ID" version="2.5">
    <display-name>Tango</display-name>

    <filter>
        <filter-name>SalsaValidationFilter</filter-name>
        <filter-class>net.semandex.salsa.validationFilters.SalsaValidationFilter</filter-class>
    </filter>

    <filter-mapping>
        <filter-name>SalsaValidationFilter</filter-name>
        <url-pattern>/*</url-pattern>
        <!-- <servlet-name>SalsaValidationServlet</servlet-name> -->
        <dispatcher>REQUEST</dispatcher>
    </filter-mapping>

    <session-config>
        <session-timeout>20</session-timeout>
    </session-config>

    <security-constraint>
        <web-resource-collection>
            <web-resource-name>Login page images</web-resource-name>
            <url-pattern>/images/salsadb-logo2.png</url-pattern>
            <url-pattern>/images/salsa-icon.png</url-pattern>
            <url-pattern>/images/shadow_box.png</url-pattern>
            <url-pattern>/images/header.png</url-pattern>
            <url-pattern>/images/bg.png</url-pattern>
            <url-pattern>/css/splash.css</url-pattern>
            <url-pattern>/WEB-INF/licenseValidation.html</url-pattern>
            <url-pattern>/auth/licenseValidation.html</url-pattern>
        </web-resource-collection>
    </security-constraint>

    <security-constraint>
        <web-resource-collection>
            <web-resource-name>The entire webapp</web-resource-name>
            <url-pattern>/*</url-pattern>
        </web-resource-collection>
        <auth-constraint>
            <role-name>SalsaUser</role-name>
        </auth-constraint>
    </security-constraint>

    <security-role>
        <role-name>SalsaUser</role-name>
    </security-role>

    <login-config>
        <auth-method>FORM</auth-method>
        <form-login-config>
          <form-login-page>/auth/login.jsp</form-login-page>
          <form-error-page>/auth/loginError.jsp</form-error-page>
        </form-login-config>

        <realm-name>mongo_login</realm-name>
    </login-config>
</web-app>

Solution

  • If you must verify the license before authentication, the only way would be using programmatic security and including the license verification as part of the process.

    Programmatic security is useful when declarative security alone is not sufficient to express the security model of an application. The API for programmatic security consists of methods of the EJBContext interface and the HttpServletRequest interface. These methods allow components to make business-logic decisions based on the security role of the caller or remote user.

    https://docs.oracle.com/javaee/7/tutorial/security-intro003.htm#BNBXH

    Here are a few short examples: https://docs.oracle.com/javaee/7/tutorial/security-webtier003.htm#GJIIE

    I haven't done programmatic security myself but from the looks of it you could do something like this:

    1. Drop container security from your web.xml -by design it will precede everything else and thus get in your way. Ideally you can just set auth-method to NONE and keep the security constraint -presumably you'll be shown the error page directly when trying to access and then you can do 2) and 3) (below) from there in servlets before trying again. If you must drop the security constraint too, then use filters as follows.
    2. Add a filter that will verify the license. It it fails, it will redirect to a page to upload the license and try again. If it doesn't, you'll do the next filter in the chain.
    3. The next filter in the chain knows the license is valid. If the user isn't logged, it will try to get the user and password as request parameters. If they exist, it will try to authenticate programmatically with them -at this point you are doing one of the examples in the previous link. If the user is logged, proceed. If the credentials don't match or there are no credentials to try, redirect to a custom login page to let the user fill in his credentials and try again.
    4. If you had to drop the security constraint from your web.xml, have another filter to check roles here and anything else you might need.

    Make sure you redirect to a different path, so those pages don't call the filters again and loop. (Filters can be configured to be skipped when forwarding/redirecting and I think that's the default, but if you had to drop the security constraint then you want to be sure they are called no matter what.)

    You might do all this in a single filter and/or instead of redirecting when failing write out the appropiate response (kinda simulating a servlet POSTing to himself several times). A filter is better for this than a servlet because you can be sure it was called for any access attempt.

    Another way would be to write everything in 2) and 3) as a single servlet aside from the "real" app and have a filter redirecting to it if the session isn't authenticated and doesn't have the proper "valid-license" attribute set (you set it in the servlet). This might be faster and perhaps simpler to maintain but not so tightly coupled.