2011-02-04 14 views
10

Prefazioneimpostazione Authentication Header in Servlet via Filtro

Questo è il mio primo tentativo di un filtro, essere gentile.

Descrizione del progetto

Sto cercando di finalizzare una build per uno SSO per molti dei nostri applicativi e mi sembra di essere colpire un muro. La webapp che sto tentando di connettersi utilizza l'intestazione "Autenticazione" per determinare le credenziali dell'utente all'interno dell'applicazione. Ho costruito un filtro con la speranza di impostare l'intestazione prima che venga passata alla webapp.

Il problema

Il codice passa convalida eclissi, compila, carichi di Tomcat, e passa attraverso la webapp. L'unica cosa che manca è l'intestazione di autenticazione.

Cosa mi sfugge/faccio male?

fonte AuthenticationFilter

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

fonte 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 Fonte

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 

Grazie per il vostro aiuto.

+3

Solo qualche consiglio; per favore non gettare Throwable, che è riservato a una moltitudine di errori che di solito vengono catturati dalla JVM solo come ultima risorsa e non prendono mai ganci o eccezioni in un filtro senza ricrearli. –

+0

@ Andrew White, lo terrò a mente. Stavo cercando di mantenere separati il ​​mio processo e gli errori senza una serie di dichiarazioni brutte. Non scusa il mio comportamento, solo lo spiega. – PseudoNinja

+0

Hai detto che questo compila, ma non riesco a trovare un metodo addHeader() su ServletRequestWrapper. – highlycaffeinated

risposta

9

Sto aggiungendo una nuova risposta, poiché è completamente diversa.

Ho eseguito un test sul mio sistema. Ho copiato il tuo codice, scaricato la cookie test e ho scritto un semplice Servlet per scaricare le cose per me.

E ha funzionato bene, salvo un avvertimento.

Non so come la tua app lo stia utilizzando. Ma il tuo ServletRequestWrapper implementa getHeaderNames e getHeader, ma NON implementa getHeaders. Mi sono imbattuto in questo problema perché ho usato getHeaders per provare a scaricare la richiesta e, ovviamente, mancava l'autorizzazione.

Quindi, si consiglia di guardare il codice più vicino per vedere se effettivamente non sta usando getHeaders. Se lo è, funzionerà "correttamente", ma salterà completamente il lavoro che hai svolto e mancherà quindi all'intestazione di autorizzazione.

Ecco la mia implementazione e ha funzionato per me.

@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); 
     } 
    } 
+0

funziona magnificamente. Grazie ragazzi. – PseudoNinja

1

In primo luogo, la domanda di base (una sorta di domanda "è questa plug-in"), presumo che i tuoi cookie siano tutti collegati allo stesso dominio e che tu non stia cercando di ottenere il comportamento cross domain qui. Perché i cookie non lo faranno.

Oltre il test dei cookie, questo sembra buono. Ma tutto dipende dal test dei cookie.

Se si desidera testare l'intestazione Autorizzazione, è possibile semplicemente cortocircuitare il test dei cookie (vale a dire che passa sempre) e compilare l'intestazione Autorizzazione con un valore valido. Questo, a breve termine, testerà l'intero schema di autorizzazione.

Una volta terminato/risolto, è possibile concentrarsi sull'impostazione e sulla consegna dei cookie.

Suppongo anche che non si stia utilizzando l'autenticazione basata su Java EE Container, con Tomcat che esegue questo controllo. In tal caso, un filtro è semplicemente "troppo tardi". Il contenitore avrà già preso le sue decisioni prima ancora che il filtro venga chiamato.

Se si utilizza l'autenticazione basata sul contenitore e le app si trovano sullo stesso contenitore, immagino che Tomcat (o qualcuno) disponga di un'opzione SSO a livello di contenitore. So che Glassfish farà questo per te fuori dalla scatola. Dovrebbe essere semplice modificare le risorse di Tomcat (cioè i meccanismi Java EE/Servlet Java non portabili) per implementare questo se è il caso.

+0

Buona risposta Will. Vorrei anche sottolineare che è consigliabile utilizzare il server delle applicazioni per l'autenticazione e l'SSO.Probabilmente sono meglio testati e più ampiamente controllati. Sono particolarmente preoccupato per le EncryptionUtils menzionate. – erloewe

+0

Normalmente sarei d'accordo con voi ragazzi ma i requisiti del progetto stabiliscono che il Login deve supportare la gestione degli utenti esistenti che è semplicemente Autenticazione di Base. L'applicazione stessa utilizza le informazioni nell'intestazione per popolare le informazioni sul sito. – PseudoNinja

+0

Non stavo suggerendo di passare all'autenticazione basata su container. Ma, per essere chiari, i contenitori supportano l'autenticazione di base. Ma dovresti cambiare la tua applicazione per usare l'interfaccia di sicurezza Servlet (isUserInRole, getPrincipal, ecc.). –