This post provides an overview of how to use Apache Shiro for securing an example web browser facing REST API based on Dropwizard.

The example REST API supports the following well known authentication mechanisms:

  • No authentication
  • HTTP Basic authentication
  • Cookie based authentication

The REST API described here uses a Dropwizard bundle for Apache Shiro: http://github.com/silb/dropwizard-shiro

The techniques described in this post can also be implemented by using Jersey without Dropwizard: http://github.com/silb/shiro-jersey

How clients use the REST API

Since the REST API supports anonymous users, authentication is optional. This means that unauthenticated requests cannot be responded with an HTTP basic challenge.

Clients that use HTTP basic authentication must therefore supply their credentials preemptively (before receiving a challenge).

Web browser clients use cookie based authentication by first issuing an HTTP POST to /session with the username and password as form parameters. Subsequent requests are then automatically authenticated as long as the session is valid.

How the Shiro bundle is configured

The Shiro bundle is added to Dropwizard as described here: http://github.com/silb/dropwizard-shiro

Optional HTTP basic authentication is implemented by overriding the Shiro HTTP basic filter so that it only sends challenge responses to authentication requests that fail:

public class OptionalBasicHttpAuthenticationFilter extends BasicHttpAuthenticationFilter {
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
        if (!isLoginAttempt(request, response)) return true;
        if (executeLogin(request, response)) return true;
        else return sendChallenge(request, response);
    }
}

The Shiro authentication filters are configured in shiro.ini:

[main]
optionalAuthBasic = OptionalBasicHttpAuthenticationFilter

[urls]
/session/** = anon
/** = noSessionCreation, optionalAuthBasic, anon

The filter noSessionCreation is used for limiting session to users that log in through the HTML form. Sessions are not used for storing anything but the session cookie ID.

The anon filter allows access for anonymous users. Both filters are part of Shiro's default filters.

Form based authentication is implemented by a JAX-RS resource that authenticates users and creates sessions:

@PATH("/session")
public class SessionResource {
    @POST
    public String login(@FormParam String username, @FormParam String password, @Auth Subject subject) {
        subject.login(new UsernamePasswordToken(username, password));
        return username;
    }
}

Note that the path of /session corresponds to the shiro.ini filter config without the noSessionCreation filter. When logging in a user, Shiro will therefore also create an HTTP session.

Dropwizard does not enable sessions by default. Sessions must be enabled during initialization of the Dropwizard service:

public class ExampleApiService extends Service<ExampleApiConfiguration> {
    public void run(ShiroJaxRsConfiguration configuration, Environment environment) throws Exception {
            environment.setSessionHandler(new SessionHandler());
        }
}

Conclusion

While Dropwizard does provide an authentication bundle, Apache Shiro is a viable alternative for:

  • organizations already using Apache Shiro and who have invested in learning and integrating it into their infrastructure.
  • applications that need authorization features like Apache Shiro's permission model.