Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Setting Authentication Header in Servlet via Filter

Preface

This is my first attempt at a Filter, be gentle.

Project Description

I am trying to finalize a build for a SSO for several of our applications and I seem to be hitting a wall. The webapp I am attempting to connect to uses the "Authentication" header to determine user credentials within the application. I have built a Filter with hopes of setting the header before it is passed on to the webapp.

The Problem

The code passes eclipse validation, compiles, loads to Tomcat, and passes through to the webapp. The only thing that is missing is the Authentication header.

What am I missing/doing wrong?

AuthenticationFilter source

package xxx.xxx.xxx.xxx.filters;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import xxx.xxx.xxx.ConfigFile;
import xxx.xxx.xxx.Console;
import xxx.xxx.xxx.FalseException;

import xxx.xxx.activity.EncryptUtil;

public class AuthenticationFilter implements Filter {
  public ConfigFile config;

  public void init(FilterConfig arg0) throws ServletException {
    config = new ConfigFile("C:/config.properties");
  }

  public void doFilter(ServletRequest sRequest, ServletResponse sResponse, FilterChain filterChain) throws IOException, ServletException {
    Console.debug("AuthenticationFilter.doFilter() triggered.");
    ServletRequestWrapper request = new ServletRequestWrapper((HttpServletRequest) sRequest);
    HttpServletResponse response = (HttpServletResponse) sResponse;
    HttpSession session = request.getSession();
    Cookie cookie = null;
    try {
      if (request.getParameter("logout") != null) {
        session.invalidate();
        throw new FalseException("Logout recieved");
      }
      String auth = request.getHeader("Authorization");
      if (auth == null) {
        Console.debug("Authorization Header not found.");
        // get cookie --COOKIE NAME--
        Cookie[] cookies = request.getCookies();
        if (cookies == null) {
          throw new FalseException("Cookies not set.");
        }
        for (int i = 0; i < cookies.length; i++) {
          if (cookies[i].getName().equals(config.getProperty("authentication.cookie.name"))) {
            cookie = cookies[i];
          }
        }
        if (cookie == null) {
          throw new FalseException("Cannot find Cookie (" + config.getProperty("authentication.cookie.name") + ") on Client");
        }
        Console.debug("Cookie (" + config.getProperty("authentication.cookie.name") + ") found on Client. value="+cookie.getValue());
        String decToken = decryptToken(cookie.getValue());
        Console.debug("Decrypted Token: "+decToken);

        Console.debug("Setting Authorization Header...");
        request.setAttribute("Authorization", decToken);
        request.addHeader("Authorization", decryptToken(cookie.getValue()));
        Console.debug("Authorization Header set.");
        Console.debug("Validating Authorization Header value: "+request.getHeader("Authorization"));
      }
    }catch (FalseException e) {
      Console.msg(e.getMessage() + ", giving the boot.");
      response.sendRedirect(config.getProperty("application.login.url"));
    } catch (Exception e) {
      Console.error(e);
    }
    Console.debug("AuthenticationFilter.doFilter() finished.");
    filterChain.doFilter(request, response);
  }

  public void destroy() {

  }

  private String decryptToken(String encToken) {
    String token = null;
    token = EncryptUtil.decryptFromString(encToken);
    return token;
  }
}

web.xml source

<web-app>
  <filter>
    <filter-name>AuthenticationFilter</filter-name>
    <display-name>AuthenticationFilter</display-name>
    <description></description>
    <filter-class>com.xxx.xxx.xxx.filters.AuthenticationFilter</filter-class>
  </filter>
  <filter-mapping>
    <filter-name>AuthenticationFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>
  ...
</web-app>

ServletRequestWrapper Source

package com.xxx.xxx.xxx.filters;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

public class ServletRequestWrapper extends javax.servlet.http.HttpServletRequestWrapper {

  public ServletRequestWrapper(HttpServletRequest request) {
    super(request);
    headerMap = new HashMap();
  }

  private Map headerMap;

  public void addHeader(String name, String value) {
    headerMap.put(name, new String(value));
  }

  public Enumeration getHeaderNames() {
    HttpServletRequest request = (HttpServletRequest) getRequest();
    List list = new ArrayList();
    for (Enumeration e = request.getHeaderNames(); e.hasMoreElements();) {
      list.add(e.nextElement().toString());
    }

    for (Iterator i = headerMap.keySet().iterator(); i.hasNext();) {
      list.add(i.next());
    }
    return Collections.enumeration(list);
  }

  public String getHeader(String name) {
    Object value;
    if ((value = headerMap.get("" + name)) != null)
      return value.toString();
    else
      return ((HttpServletRequest) getRequest()).getHeader(name);
  }

}

Debug Log

LoginServlet.doGet() triggered.
[DEBUG] : Authenticate.isClientLoggedIn() triggered.
xxx url : https://xxx.xxx.xxx/xxx/home.action
[DEBUG] : Authenticate.isClientLoggedIn() status code: 401
Unauthorized User.
Client IS NOT logged in.

-- Fill out Login Form, submit --

LoginServlet.doPost() triggered.
[DEBUG] : Authenticate.isClientLoggedIn() triggered.
xxx url : https://xxx.xxx.xxx./xxx/home.action
[DEBUG] : Authenticate.isClientLoggedIn() status code: 401
Unauthorized User.
Client IS NOT logged in.
Client (--USERID--) attempting basic authentication with password(--PASSWORD--).
[DEBUG] : BasicAuthentication.touch(http://localhost:PORT/vu/loginCheck.html, --USERID--, --PASSWORD--) triggered.
[DEBUG] : BasicAuthentication.touch() response code: 200
Client (--USERID--) has been logged IN.
Client (--USERID--) basic authentication finished, Client is logged in.
Client (--USERID--) logged in successfully.
[DEBUG] : Cookie (xxx_token) Set: 1e426f19ebdfef05dec6544307addc75401ecdc908a3c7e6df5336c744--SECRET--
[DEBUG] : Redirecting client to https://xxx.xxx.xxx/xxx/home.action

-- Redirected to webapp, filter recieves --

[DEBUG] : AuthenticationFilter.doFilter() triggered.
[DEBUG] : Authorization Header not found. << Initical check to see if user is already logged in to site
[DEBUG] : Cookie (xxx_token) found on Client. value=1e426f19ebdfef05dec6544307addc75401ecdc908a3c7e6df5336c744--SECRET--
[DEBUG] : Decrypted Token: Basic --SECRET--
[DEBUG] : Setting Authorization Header...
[DEBUG] : Authorization Header set.
[DEBUG] : Validating Authorization Header value: Basic --SECRET-- << Value matches Decrypted Token
[DEBUG] : AuthenticationFilter.doFilter() finished.

-- Web Application errors out, unable to find Authorization header 

Thanks for your help.

like image 822
PseudoNinja Avatar asked Feb 04 '11 15:02

PseudoNinja


1 Answers

First, the most basic question (kind of an "is this plugged in" question), I assume that your cookies are all rooted in the same domain, and that you're not trying to get cross domain behavior here. Because cookies won't do that.

Beyond the cookie test, this looks fine. But it all hinges on the cookie test.

If you want to test the Authorization header, then you can simply short circuit the cookie test (i.e. it always passes) and populate the Authorization header with some valid value. This will, in the short term, test your whole Authorization scheme.

Once that's done/fixed, then you can focus on the cookie setting and delivery.

I also assume that you're not using Java EE Container based authentication, with Tomcat doing this check for you. In that case, a filter is simply "too late". The container will have already made it's decisions before your filter even gets called.

If you are using container based authentication, and your apps are on the same container, I would imagine Tomcat (or someone) has an SSO option at the container level. I know that Glassfish will do this for you out of the box. It should be straightforward to modify Tomcat artifacts (i.e. not portable Java EE/Servlet mechanisms) to implement this if that is the case.

like image 124
Will Hartung Avatar answered Oct 19 '22 06:10

Will Hartung