Настройка заголовка проверки подлинности в сервлете через фильтр

Введение

Это моя первая попытка фильтра, будьте осторожны.

Описание проекта

Я пытаюсь завершить сборку для SSO для нескольких наших приложений, и я, кажется, ударяю о стену. Webapp, с которым я пытаюсь подключиться, использует заголовок "Аутентификация" для определения учетных данных пользователя в приложении. Я создал фильтр с надеждой установить заголовок, прежде чем он будет передан в webapp.

Проблема

Код передает проверку затмения, компилирует, загружает в Tomcat и проходит через webapp. Единственное, чего не хватает, это заголовок Authentication.

Что мне не хватает/что-то не так?

Источник проверки подлинности

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

<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

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);
  }

}

Журнал отладки

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 

Спасибо за вашу помощь.

Ответ 1

Я добавляю новый ответ, так как он совершенно другой.

Я проверил свою систему. Я скопировал ваш код, сбросил тестовый файл cookie и написал простой сервлет, чтобы свалить все на меня.

И он работал нормально, за исключением одного оговорки.

Я не знаю, как ваше приложение использует это. Но ваш ServletRequestWrapper реализует getHeaderNames и getHeader, но НЕ реализует getHeaders. Я столкнулся с этой проблемой, поскольку использовал getHeaders, чтобы попытаться сбрасывать запрос, и, конечно же, авторизация отсутствовала.

Итак, вы можете посмотреть на свой код ближе, чтобы убедиться, что он действительно не использует getHeaders. Если это так, он "отлично работает", но полностью пропустит проделанную работу и, таким образом, пропустит ваш заголовок авторизации.

Здесь моя реализация, и это сработало для меня.

    @Override
    public Enumeration getHeaders(String name) {
        Enumeration e = super.getHeaders(name);
        if (e != null && e.hasMoreElements()) {
            return e;
        } else {
            List l = new ArrayList();
            if (headerMap.get(name) != null) {
                l.add(headerMap.get(name));
            }
            return Collections.enumeration(l);
        }
    }

Ответ 2

Во-первых, самый основной вопрос (вроде вопроса "это подключен" ), я предполагаю, что ваши файлы cookie все внедрены в одном домене и что вы не пытаетесь получить здесь перекрестное поведение. Потому что cookie не сделает этого.

Помимо теста cookie, это выглядит отлично. Но все это зависит от теста cookie.

Если вы хотите протестировать заголовок авторизации, вы можете просто закоротить тест cookie (т.е. он всегда проходит) и заполнить заголовок авторизации некоторым допустимым значением. Это в краткосрочной перспективе проверит вашу схему авторизации.

Как только это будет сделано/исправлено, вы можете сфокусироваться на настройке и доставке файлов cookie.

Я также предполагаю, что вы не используете аутентификацию на основе Java EE Container, и Tomcat делает эту проверку для вас. В этом случае фильтр просто "слишком поздно". Контейнер уже принял решения до того, как ваш фильтр даже получит вызов.

Если вы используете аутентификацию на основе контейнеров, а ваши приложения находятся в одном контейнере, я бы предположил, что у Tomcat (или у кого-то) есть опция SSO на уровне контейнера. Я знаю, что Glassfish сделает это за вас из коробки. Это должно быть просто изменить артефакты Tomcat (т.е. Не переносимые механизмы Java EE/Servlet) для реализации этого, если это так.