Saturday, November 9, 2013

Servlet to handle basic auth login request


One of the simple servlets that you might have seen is a servlet
that accepts basic auth header (WWW-Authenticate) and gives
the user a security session. This is a simple implementation of
one such servlet. It checks if the request already has a basic auth
header. If not it asks client to send one. If basic auth header is
present that is used to authenticate the user and return the user a
single sign on token in the form of a cookie or a String.

This servlet can be tested from browser either by entering credentials
manually in the basic auth credential prompt. It can also be called
from soapui.Soapui can create the basic auth header if user supplied
the username and password.

This uses one of classes named AuditRecord I wrote in my previous posts 
(link here)

import java.io.IOException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.codec.binary.Base64;

public class BasicAuthLoginServlet extends HttpServlet {

    private static final long serialVersionUID = -6527953079135467056L;

    private static final String SSO_COOKIE_NAME = "yourcompany-sso";
    private static final String SSO_COOKIE_DOMAIN = ".yourcompany.com";
    private static final String BASIC_AUTH_HEADER_NAME = "WWW-Authenticate";
    private static final String BASIC_AUTH_HEADER_VALUE = "BASIC realm=\"basic_realm\"";

    private static final String SERVLET_NAME = "BasicAuthLoginServlet";

    private final static Pattern basicCredentialPattern = Pattern
            .compile("([^:]+):(.+)");

    protected void doGet(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {

        final AuditRecord auditRecord = new AuditRecord(request, SERVLET_NAME);
        final String auth = request.getHeader("Authorization");

        try {
            // if user hasn't sent the basic auth info, ask him
            if (auth == null) {
                auditRecord.append("auth header is null");
                response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
                response.addHeader(BASIC_AUTH_HEADER_NAME,
                        BASIC_AUTH_HEADER_VALUE);
                return;
            }

            // authenticate user
            String ssoToken = loginWithBasic(auth, auditRecord);

            // if ssoToken === null that means authentication failed - send 401
            if (ssoToken == null) {
                response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
                return;
            }

            // if it has come here that means user has authenticated
            // successfully
            auditRecord.append("authentication successful");

            // if it has come here that means authentication and authorization
            // is successful - send 200
            auditRecord.append("success");
            setSingleSignonCookie(response, "token");
            sendSuccess(response);
        } finally {
            auditRecord.log();
        }

    }

    private void sendSuccess(HttpServletResponse response) throws IOException {
        response.setStatus(HttpServletResponse.SC_OK);
        response.setContentType("text/plain");
        response.getWriter().write("Login successful");
    }

    private void setSingleSignonCookie(HttpServletResponse response,
            String ssoToken) {
        final Cookie cookie = new Cookie(SSO_COOKIE_NAME, ssoToken);
        cookie.setDomain(SSO_COOKIE_DOMAIN);
        cookie.setPath("/");
        cookie.setSecure(true);

        response.addCookie(cookie);
    }

    /**
     * 
     * @param authHeader
     * @param auditRecord
     * @return if authenticated returns a token that represents user's
     *         singlesignontoken
     */
    private String loginWithBasic(String authHeader, AuditRecord auditRecord) {

        String username = null;
        String password = null;

        String result = null;

        if (authHeader != null && authHeader.toUpperCase().startsWith("BASIC ")) {
            String input = new String(Base64.decodeBase64(authHeader.substring(
                    6).getBytes()));
            Matcher m = basicCredentialPattern.matcher(input);
            if (m.find()) {
                username = m.group(1);
                password = m.group(2);
            }
        }

        if (username != null && password != null) {
            auditRecord.append("username", username);
            // call some db or ldap and authenticate using your own logic and
            // give user a single sing on token
            if (username.equals("test") && password.equals("test")) {
                result = "dummySSOToken";
            }
        }

        return result;

    }
}

No comments:

Post a Comment