Roy Tang

Programmer, engineer, scientist, critic, gamer, dreamer, and kid-at-heart.

Blog Notes Photos Links Archives About

I want to write a servlet that wraps around a set of resources and needs to protect them with basic HTTP auth; the submitted username/pass will be checked against the backend database before serving the file.

Does anyone have any working examples of this? I tried the sample at http://www.coderanch.com/t/352345/Servlets/java/HTTP-basic-authentication-Web-Applications but it kept returning an IllegalStateException in the sendError call.

Comments

I just used the sample in the link.
@Roy, example in your post worked fine for me. I’m not sure why its giving error for you. could you please update your post with the stacktrace?
IMO, a servlet filter is more appropriate for this, as it is independent of the servlet and can be applied to multiple servlets if necessary. This may be more advanced than what you need presently, so please consider this only as a friendly pointer, not a critique.

Here is some code that returns a Credential object (bean object holding login and password).

public Credentials credentialsWithBasicAuthentication(HttpServletRequest req) {
    String authHeader = req.getHeader("Authorization");
    if (authHeader != null) {
        StringTokenizer st = new StringTokenizer(authHeader);
        if (st.hasMoreTokens()) {
            String basic = st.nextToken();

            if (basic.equalsIgnoreCase("Basic")) {
                try {
                    String credentials = new String(Base64.decodeBase64(st.nextToken()), "UTF-8");
                    LOG.debug("Credentials: " + credentials);
                    int p = credentials.indexOf(":");
                    if (p != -1) {
                        String login = credentials.substring(0, p).trim();
                        String password = credentials.substring(p + 1).trim();

                        return new Credentials(login, password);
                    } else {
                        LOG.error("Invalid authentication token");
                    }
                } catch (UnsupportedEncodingException e) {
                    LOG.warn("Couldn't retrieve authentication", e);
                }
            }
        }
    }

    return null;
}

It works well, even with a password as funky as :&=/?é$£.

Here is a basic unit test for the class, using jMock:

public void testCredentialsWithBasicAuthentication() {
    // Setup
    final HttpServletRequest request = context.mock(HttpServletRequest.class);
    
    AuthentificationHelper helper = new AuthentificationHelper();
    String login = "mickael";
    String password = ":&=/?é$£";
    String base64Hash = Base64.encodeString(login + ":" + password);
    final String authHeader = "Basic " + base64Hash;
    
    // Expectations
    context.checking(new Expectations() {
        {
            oneOf (request).getHeader("Authorization");
            will(returnValue(authHeader));
        }   
    });
    
    // Execute
    Credentials credentials = helper.credentialsWithBasicAuthentication(request);
    
    // Verify
    assertNotNull(credentials);
    assertEquals(login, credentials.getLogin());
    assertEquals(password, credentials.getPassword());
    
    context.assertIsSatisfied();
}