Cloud with rain
.:G
G:.
0 and 1 serie, black on white
pulled card
myjsp.feelinglinux.com
ver. 1.1.9-4
Hallo, welcome to my world.
Here you can find some stuff about computer science.
<<< Enjoy your visit! >>>
0 and 1 serie, white on black

Apache Tomcat: Implementazione di un "Valve"

        Scritto: Giansante Gabriele, 20/11/2016     

Usati: Java 1.8, Apache Tomcat 8, Linux CentOS 6.5

Vedremo cos'e' un "valve", come realizzarne uno, come configurarlo su Tomcat.

Cos'e' un valve

Un "valve" (o valvola, forzando un po' in italiano) e' un componente che viene attivato durante l'elaborazione delle richieste verso Tomcat.
Consente di manipolare il flusso normale di una richiesta, ad esempio (ma non solo), bloccandolo (per es. con response HTTP 403), facendo il redirect ad altre pagine o anche semplicemente analizzando i dati in ingresso.
Il valve viene inserito nella catena di elaborazione di una richiesta indirizzata verso un container (che sia un context, un host, o l'intero engine). Riceve la coppia request e response (servlet request, servlet response), originale o manipolata da altri valve a monte della catena, effettua le sue elaborazioni ed eventualmente rigira il tutto intatto o ulteriormente manipolato al valve successivo.
Alcune applicazioni dei valve possono essere
  • blocco richieste in base ad ip sorgente, user agent, ecc.
  • reindirizzamento automatico
  • gestione statistiche sulle visite
  • gestione autenticazione
  • inserimento al volo di regole firewall per richieste reputate maligne
  • logging di vario tipo


Valve predefiniti

Esistono valve predefiniti e specializzati per diversi compiti. Si puo' pensare di estendere uno di essi oppure di partire da una classe base che implementi gia' tutto tranne l'elaborazione specifica che vogliamo avere.

Prendendo a riferimento "Tomcat 8", alcuni valve gia' presenti (ma non tutti) sono:
  • Base Valve (org.apache.catalina.valves.ValveBase)
    Container: Engine, Host, Context
    Serve ad implementare "valve" adatti alle proprie esigenze. Non puo' essere usata direttamente in quanto classe astratta, ma va estesa secondo le proprie necessita'.
    E' quello che useremo piu' avanti per gli scopi dell'articolo


  • Access Log Valve (org.apache.catalina.valves.AccessLogValve)
    Container: Engine, Host, Context
    Permette la registrazione dei dati relativi ad ogni richiesta ricevuta


  • Remote Address Filter (org.apache.catalina.valves.RemoteAddrValve)
    Container: Engine, Host, Context
    Rende possibile filtrare (bloccare o permettere) le richieste in base all'indirizzo IP del client, tramite utilizzo di espresisoni regolari


  • Remote Host Filter (org.apache.catalina.valves.RemoteHostValve)
    Container: Engine, Host, Context
    Rende possibile filtrare (bloccare o permettere) le richieste in base all'host del client, tramite utilizzo di espresisoni regolari


  • Single Sign On Valve (org.apache.catalina.authenticator.SingleSignOn)
    Container: Host
    Consente l'implementazione dell'autenticazione unica per tutte le applicazioni WEB dello stesso virtual host


  • Basic Authenticator Valve (org.apache.catalina.authenticator.BasicAuthenticator)
    Container: Context
    Aggiunge l'autenticazione HTTP di base (la piu' semplice, uso realm e trasmissione non criptata di utente e password)


  • Form Authenticator Valve (org.apache.catalina.authenticator.FormAuthenticator)
    Container: Context
    Aggiunge l'autenticazione tramite FORM (pagina o blocco con form di login)


  • SSL Authenticator Valve (org.apache.catalina.authenticator.SSLAuthenticator)
    Container: Context
    Aggiunge l'autenticazione mediante SSL (Secure Socket Layer)


  • Error Report Valve (org.apache.catalina.valves.ErrorReportValve)
    Container: Host, Context
    Visualizza un report HTML come HTTP response (se il parametro "showReport" e' true) in seguito ad un errore nell'elaborazione della richiesta. Puo' includere informazioni sul server (se abilitate) ed uno stack-trace parziale


  • Crawler Session Manager Valve (org.apache.catalina.valves.CrawlerSessionManagerValve)
    Container: Engine, Host, Context
    Utile per trattare le richieste multiple dei BOT (Google, Baidu, Bing, ecc.) tutte all'interno della stessa sessione, se possibile, in modo da ottimizzare le risorse utilizzate. Individua i BOT tramite espressioni regolari che definiscono lo "user-agent"


  • Rewrite Valve (org.apache.catalina.valves.rewrite.RewriteValve)
    Container: Engine, Host, Context
    Cerca di replicare il comportamento del "mod_rewrite" per "Apache httpd server" su Tomcat. Utilissimo per filtrare le richieste HTTP in base a vari campi (query, pagina, user agent, referer, ecc.)
Per approfondire l'utilizzo dei valve predefiniti, leggere (per Tomcat 8, ultima release): The Valve Component.

Vogliamo vedere come realizzare un nostro valve, quindi ci concentreremo sul primo, "Base Valve".

Realizzazione di un valve

Realizzare un proprio valve e' semplice ed avviene sostanzialmente mediante i passaggi seguenti:
  1. Estendere la classe "ValveBase"
    "org.apache.catalina.valves.ValveBase"
    https://tomcat.apache.org/tomcat-8.0-doc/api/org/apache/catalina/valves/ValveBase.html

  2. Implementare il metodo astratto "invoke" con firma
    "abstract void invoke(Request request, Response response)"

  3. Sovrascrivere, se necessario, il metodo "initInternal" con firma
    "protected void initInternal()"

  4. Implementare i metodi necessari alla gestione dei parametri nel tag di configurazione "<Valve>"

  5. Compilare il valve e renderlo visibile nel modo piu' opportuno

  6. Configurare il valve nel container opportuno

L'implementazione del valve dovrebbe essere leggera e non troppo onerosa in termini di tempo di elaborazione e di risorse utilizzate. Bisogna tener presente che il nostro valve e' solo un anello di una catena che sarebbe bene percorrere nel minor tempo possibile.
Detto questo, vediamo l'ossatura di una possibile implementazione:
package org.giansante.tomcat.test;

import java.io.IOException;
import javax.servlet.ServletException;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.valves.ValveBase
import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.Response;

/**
 * Possibile schema di una classe implementante un valve.
 * Chiamo il valve "TestValve".
 * P.S. Classe ValveBase:
 *         public abstract class ValveBase
 *         extends LifecycleMBeanBase
 *         implements Contained, Valve
 */
public class TestValve extends ValveBase {

    // ---------------------------
    // ATTRIBUTI DI CONFIGURAZIONE
    // ---------------------------
    // (facoltativo)

    private String testAttribute1 = "";
    //...
    private String testAttributeN = "";

    /**
     * Lettura dalla configurazione (tag <Valve>)
     * dell'attributo "testAttribute1"
     */
    public void setTestAttribute1(String value){
        testAttribute1 = value;
    }

    //...

    /**
     * Lettura dalla configurazione (tag <Valve>)
     * dell'attributo "testAttributeN"
     */
    public void setTestAttributeN(String value){
        testAttributeN = value;
    }

    // ------------------------------
    // INIZIALIZZAZIONE SUPPLEMENTARE
    // ------------------------------
    // (facoltativo)

    @Override
    protected void initInternal() throws LifecycleException {
        super.initInternal(); 

        //Inserire qui eventuali inizializzazioni 
        //aggiuntive richieste dal nostro valve.
        //Non serve sovrascrivere il metodo se non ce n'e' reale necessita'
    }
        
    // ---------------------------
    // ELABORAZIONE PERSONALIZZATA
    // ---------------------------
    // (obbligatorio)

    @Override
    public void invoke(Request request, Response response) throws ServletException {
        //1) Elaborazione relativa al nostro valve

        //2) Uno fra 
            //a) Chiusura con response HTTP appropriato 
            //   (redirect, autorizzazione negata, ecc.)
            //b) Chiusura con passaggio all'elaborazione del 
            //   valve successivo nella catena
    }
}


Pubblicita'
Inizializzazione del valve

Un primo modo per inizializzare il valve e' sovrascrivere il metodo "initInternal" (derivato dalla classe "org.apache.catalina.util.LifecycleBase"), chiamato una sola volta quando il valve viene istanziato ed inserito nella catena del container su cui e' configurato.
Se il valve e' stato configurato su piu' container (ad esempio su 2 o piu' virtual host diversi), allora verranno create piu' istanze, con invocazione del metodo una volta per ognuna di esse.
Possiamo usarlo, per esempio, per caricare un file esterno contenente la configurazione.

Un secondo modo per inizializzare il valve e' attraverso gli attributi passati col tag "<Valve>" (vedere "configurazione valve").
La classe implementante il valve riceve il valore degli attributi mediante invocazione di una serie di metodi "set*", uno per ogni attributo specificato, dove "*" rappresenta il nome dell'attributo con la prima lettera maiuscola. Ad esempio, se volessimo passare al valve gli attributi "host", "port", "configPath" e "debug" scriveremmo nella configurazione qualcosa del tipo:
    <Valve [...]
           host="192.168.20.56"
           port="20000"
           configPath="/test/tomcat/testValve/config.properties"
           debug="true"
           [...]/>

e dovremmo inserire nella classe il seguente codice per vederli e poterli utilizzare:
    [...]

    //Valori di default (casuali) nel caso in cui 
    //non inserisca gli attributi nella configurazione
    private String host = "127.0.0.1";
    private String port = "30000;
    private String configPath = "config.properties";
    private String debug = "false";

    [...]

    //Metodi chiamati automaticamente per 
    //impostare gli attributi configurati
    public void setHost(String host){
        this.host = host;
    }
    public void setPort(String port){
        this.port = port;
    }
    public void setConfigPath(String configPath){
        this.configPath = configPath;
    }
    public void setDebug(String debug){
        this.debug = debug;
    }
    
    [...]
Pubblicita'

Elaborazione: il metodo "invoke"

Una volta che il valve e' stato inizializzato ed inserito nella catena del container, tramite il metodo "invoke" e' pronto a ricevere ed elaborare le richieste man mano che si presentano.

Il compito del metodo "invoke" e'
  1. esaminare la richiesta cosi' come ricevuta dal valve precedente nella catena
  2. elaborare la richiesta, se necessario (con un uso sano ed ottimizzato delle risorse)
  3. attivare il valve successivo nella catena, oppure
  4. interrompere la catena, se reputato indispensabile
Il metodo "invoke" riceve in ingresso gli oggetti "request" (servlet request, istanza di "Request") e "response" (servlet response, istanza di "Response") tramite i quali possiamo ottenere tutte le informazioni utili relative alla richiesta e decidere che percorso seguira'.

Vediamo qualche esempio di cio' che possiamo fare all'interno di "invoke".
Da "request" (https://tomcat.apache.org/tomcat-8.0-doc/api/org/apache/catalina/connector/Request.html) ricaviamo i dati della richiesta che ci servono, alcuni dei quali mostrati di seguito:
    /* Queste sono SOLO ALCUNE fra le tante 
     * informazioni che possono essere ricavate dalla "request".
     * Ovviamente non sono le uniche.
     *
     * Come esempio (vedi singoli commenti), suppongo di avere 
     * una richiesta "GET", con protocollo "HTTP 1.1",
     * verso la pagina (host "www.test.com", contesto "prova")
     * "http://www.test.com:8080/prova/pagina.jsp",
     * proveniente dal client con ip "192.168.10.45" 
     * attraverso l'uso di un browser Firefox.
     * L'accesso a "pagina.jsp" avviene tramite link posto 
     * sulla pagina 
     * "https://www.test2.com/index.html"
     */

    HttpServletRequest httpServletRequest = request.getRequest();

    //Es. "GET"
    String reqMethod = httpServletRequest.getMethod();

    //Protocollo con cui soddisfare la richiesta
    //Es. "HTTP/1.1"
    String protocol = httpServletRequest.getProtocol();

    //Host oggetto della richiesta
    //Es. "www.test.com:8080" (nome casuale)
    String host = httpServletRequest.getHeader("host");

    //Contesto dell'applicazione
    //Es. "/prova"
    String context = httpServletRequest.getContextPath();

    //Percorso che include il contesto
    //Es. "/prova/pagina.jsp"
    String page = httpServletRequest.getRequestURI();

    //Percorso all'interno del contesto
    //Es. "/pagina.jsp"
    String servletPath = httpServletRequest.getServletPath();

    //Parte relativa alla query (parametri)
    //Es. "n=3&m=6"
    String query = httpServletRequest.getQueryString();

    //Eventuale risorsa con collegamento 
    //all'oggetto di questa richiesta
    //Es. "https://www.test2.com/index.html" (nome casuale)
    String referrer = httpServletRequest.getHeader("referer");

    //Agent con cui il client ha effettuato la richiesta
    //Es. "Mozilla/5.0 (Windows NT 10.0; WOW64; rv:49.0) Gecko/20100101 Firefox/49.0"
    String agent = httpServletRequest.getHeader("User-Agent");

    //Indirizzo del client
    //Es. "192.168.10.45"
    String ip = httpServletRequest.getRemoteAddr();

    //Eventuali cookie passati con la richiesta
    //javax.servlet.http.Cookie
    Cookie[] cookies = httpServletRequest.getCookies();

Con l'oggetto "response" (https://tomcat.apache.org/tomcat-8.0-doc/api/org/apache/catalina/connector/Response.html) possiamo, volendo, anche reindirizzare la richiesta verso un altro url oppure interrompere la catena restituendo un codice HTTP d'uscita come il 403 (accesso vietato), il 404 (non trovato), ecc.:
    /* Esempi mutuamente esclusivi di utilizzo 
     * dell'oggetto "response"
     */

    //-------------------
    //Risposta con errore
    //-------------------
    //Rispondere con un errore "accesso negato" al 
    //termine dell'esecuzione dell'"invoke".
    //Sono possibili ulteriori elaborazioni (log, ecc.).
    //sendError(int status, String message)
    response.sendError(403, "Accesso negato...");

    //-----------------------------
    //Risposta con reindirizzamento
    //-----------------------------
    //Reindirizzare la richiesta al termine 
    //dell'esecuzione dell'"invoke".
    //Sono possibili ulteriori elaborazioni (log, ecc.).
    //sendRedirect(String location, int status)
    //sendRedirect(String location)
    response.sendRedirect("/altraPagina.jsp")

Se non abbiamo alcun bisogno di generare un "response" (vedi esempi precedenti su "response"), al termine della nostra elaborazione dobbiamo passare il controllo al prossimo valve della catena, ammesso che ne esista uno. Non bisogna passare il controllo al valve successivo se e' stato gia' generato un response.
    [...]
    import org.apache.catalina.Valve;
    [...]

    public void invoke(Request request, Response response) throws ServletException {
        [...]
        //Prossimo valve della catena
        Valve nextValve = getNext();
        if (nextValve!=null) {
            //Se esiste un prossimo valve, lo attivo
            //Non va fatto se ho gia' generato un response
            //(error, redirect, ecc.)
            nextValve.invoke(request, response);    
        }
        [...]
    }

Il valve e' configurato su un container. Potremmo aver bisogno, per i nostri scopi, di ottenere informazioni relative al container o comunque effettuare operazioni su di esso. La classe "ValveBase" ci fornisce la variabile protetta "container" di tipo "org.apache.catalina.Container". A seconda del tipo di container su cui abbiamo configurato il valve, la variabile "container" sara' un'istanza di una delle implementazioni delle seguenti interfacce che estendono "Container": .
org.apache.catalina.Context,
org.apache.catalina.Engine,
org.apache.catalina.Host
Ulteriori informazioni possono essere reperite tramite i seguenti link:
Container: https://tomcat.apache.org/tomcat-8.0-doc/api/org/apache/catalina/Container.html
Context https://tomcat.apache.org/tomcat-8.0-doc/api/org/apache/catalina/Context.html
Engine https://tomcat.apache.org/tomcat-8.0-doc/api/org/apache/catalina/Engine.html
Host https://tomcat.apache.org/tomcat-8.0-doc/api/org/apache/catalina/Host.html
Pubblicita'

Log operazioni

Tutto quello che facciamo all'interno del valve Potremmo volerlo tracciare mediante scrittura di log.
La classe che stiamo estendendo (ValveBase) mette a disposizione la variabile protetta "containerLog" di tipo "org.apache.juli.logging.Log" che rappresenta il logger configurato per il container in cui e' stato inserito il valve (per default i log finiscono in "catalina.out").
Un esempio di scrittura di log:
    /**
     * Possibile metodo scrittura log 
     * nella nostra classe TestValve.
     * Non e' necessario creare un metodo apposito, 
     * io l'ho fatto per comodita'
     * @param text Testo da scrivere
     */
    private void log(String text){
        if (containerLog.isInfoEnabled()) {
            containerLog.info(" | " + text);
        }
    }

Il metodo "log" produrra' un output del tipo
20-Nov-2016 16:40:09.154 INFO [...] org.giansante.tomcat.test.TestValve.log  | Testo prodotto dal valve

Oltre al livello "info" sono utilizzabili i livelli "debug", "error", "fatal", "trace", "warn", inoltre tutti i metodi di scrittura hanno entrambe le firme seguenti:
    public void <livello>(Object o)
    public void <livello>(Object o, Throwable thrwbl)


Configurazione del valve

Il valve viene configurato a livello di container, in ordine di apparizione nella catena di valve (ordine assicurato per quelli configurati direttamente nel container attraverso il file di configurazione).
Puo' essere configurato nei container "engine", "host" o "context" usando il tag "Valve" con almeno l'attributo "className" (nome completo di package della classe implementante il valve).
Oltre all'attributo "className" va inserito anche ogni attributo proprio del nostro valve.
Esempi di configurazione del nostro valve "TestValve" nel file "server.xml":
    <!-- file <TOMCAT_HOME>/config/server.xml -->

    <!-- esempio su Engine -->
    <Engine [...]>
        <Valve 
               className="org.giansante.tomcat.test.TestValve"
               host="192.168.20.56"
               port="20000"
               configPath="/test/tomcat/testValve/config.properties"
               debug="true"
               [...]/>
        [...]
        
        <!-- esempio su Host -->
        <Host [...]>
            <Valve [...]
                   className="org.giansante.tomcat.test.TestValve"
                   host="192.168.20.56"
                   port="20000"
                   configPath="/test/tomcat/testValve/config.properties"
                   debug="true"
                   [...]/>
            [...]
            
            <!-- esempio su Context -->
            <Context [...]>
                <Valve [...]
                       className="org.giansante.tomcat.test.TestValve"
                       host="192.168.20.56"
                       port="20000"
                       configPath="/test/tomcat/testValve/config.properties"
                       debug="true"
                       [...]/>
                [...]
    


Esempio pratico

A questo punto dovremmo avere tutti gli elementi per creare un semplice valve che chiameremo, con molta fantasia, "ExampleValve".
I requisiti minimi per la realizzazione del valve sono:
  1. Redirect delle pagine (es. tutte le richieste "/negozio/*" a "/nuovoNegozio/*", con "*" una qualsiasi risorsa)
  2. Cartella sorgente e di destinazione del redirect configurabili nel tag "Valve"
  3. Possibilita' di blocco di User-Agent definiti dall'utente (es. Edge, MSIE, Firefox, Google Bot, Bing, ecc.)
  4. Configurazione degli agent da bloccare mediante espressioni regolari salvate in un file di properties esterno
  5. Il file di configurazione degli agent deve essere indicato nel tag "Valve"
Per quanto riguarda il "redirect", stabiliamo di avere due parametri:
  • oldPrefix
    Parte iniziale del percorso della pagina richiesta. Verra' sostituita da "newPrefix"
  • newPrefix
    Parte iniziale del percorso destinatario della redirezione. Sostituira' "oldPrefix"
Il blocco degli agent avviene tramite espressioni regolari conservate in un file di properties. Decidiamo di complicare un po' le cose (ma non troppo) stabilendo che le proprieta' la cui chiave inizia con "_" rappresentano espressioni per il confronto "case sensitive", ovvero che tiene conto di lettere maiuscole e minuscole. Creiamo allo scopo il file "/opt/test/exampleValveConfig.properties" (o con qualsiasi altro nome e/o posizione si voglia) con il seguente contenuto:
    # test configurazione agent per "ExampleValve"

    # Browser Internet explorer. Case sensitive.
    _agent1=.*MSIE.*
    # Browser Edge. Case insensitive.
    agent2=.*edge.*
    # BOT del motore di ricerca Bing. Case insensitive.
    agent3=.*bingbot.*
In base a quanto detto, possiamo gia' inserire la configurazione (anche se non abbiamo ancora visto la classe). Modificare il file tomcat "server.xml" aggiungendo il valve nel container "host":
    <!-- server.xml -->
    [...]
    <host [...]>
        <Valve className="org.giansante.tomcat.test.ExampleValve"
                        oldPrefix="/negozio"
                        newPrefix="/nuovoNegozio"
                        configPath="/opt/test/exampleValveConfig.properties"
        />
Non rimane che scrivere la classe implementante il valve:
package org.giansante.tomcat.test;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.Response;
import org.apache.catalina.valves.ValveBase;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import org.apache.catalina.Valve;

/**
 * Esempio di valve creato dall'utente
 * @author gabriele
 */
public class ExampleValve extends ValveBase {

    //Contiene le espressioni regolari con le relative chiavi
    private final ConcurrentHashMap<String, Pattern> blockAgentPatterns = new ConcurrentHashMap();

    // ---------------------------
    // ATTRIBUTI DI CONFIGURAZIONE
    // ---------------------------
    // Attributi tag <Valve>

    private String configPath = "exampleValveConfig.properties";
    private String oldPrefix = "";
    private String newPrefix = "";

    public void setConfigPath(String path){
        configPath = path;
    }
    public void setOldPrefix(String oldPrefix){
        this.oldPrefix = oldPrefix;
    }
    public void setNewPrefix(String newPrefix){
        this.newPrefix = newPrefix;
    }
    
    // ------------------------------
    // INIZIALIZZAZIONE SUPPLEMENTARE
    // ------------------------------

    @Override
    protected void initInternal() throws LifecycleException {
        super.initInternal(); 
        //Caricamento delle espressioni regolari dal file di configurazione
        try {
            Properties config = new  Properties();
            try (FileInputStream stream = new FileInputStream(new File(configPath))) {
                //Caricamento file di configurazione
                config.load(stream);
                
                //Caricamento pattern per il blocco
                blockAgentPatterns.clear();
                for (Object key: config.keySet()){
                    String sKey = (String)key;
                    
                    //Se la chiave ha "_" davanti, va considerata case sensitive
                    boolean caseInsensitive = !sKey.startsWith("_");
                    //Opzioni match: UNICODE + CASE SENSITIVE o INSENSITIVE
                    int options = Pattern.UNICODE_CASE | (caseInsensitive ? Pattern.CASE_INSENSITIVE : 0);
                    //Compilazione ed aggiunta delle espressioni regolari per un utilizzo successivo
                    blockAgentPatterns.put(sKey, Pattern.compile(config.getProperty(sKey).trim(), options));
                }
            }
        } catch (FileNotFoundException e) {
                containerLog.error(e.getMessage());
        } catch (IOException e) {
                containerLog.error(e.getMessage());
        }
    }
        
    // ---------------------------
    // ELABORAZIONE PERSONALIZZATA
    // ---------------------------
    // (obbligatorio)

    /**
     * Verifica che la stringa passata rispetti almeno una 
     * delle espressioni regolari caricate.
     * @param agent stringa da verificare
     * @return true se c'e' il match con almeno una espressione 
     *         regolare, false altrimenti
     */
    private boolean matchPattern(String agent){
        boolean result = false;
        String textToMatch = agent!=null ? agent.trim() : "";
        Matcher m;
        for (String patternName : blockAgentPatterns.keySet()){
            m = blockAgentPatterns.get(patternName).matcher(textToMatch);
            if (m.matches()) {
                result = true;
                //Log del fatto che l'agent corrisponde ad 
                //almeno una espressione
                containerLog.info("Trovato Agent proibito (PATTERN=["+patternName+"]; EXPR=["+blockAgentPatterns.get(patternName).pattern()+"])");
                break;
            }
        }
        return result;
    }
    
    @Override
    public void invoke(Request request, Response response) throws ServletException, IOException {
        HttpServletRequest httpServletRequest = request.getRequest();
        //Percorso pagina (senza contesto)
        String servletPath = httpServletRequest.getServletPath();
        //User Agent
        String agent = httpServletRequest.getHeader("User-Agent");
        if (matchPattern(agent)){
            //Respingo la richiesta
            response.sendError(403, "Accesso negato");
        } else if (servletPath.startsWith(oldPrefix)){
            //Rimando all'indirizzo corretto
            response.sendRedirect(newPrefix + servletPath.substring(oldPrefix.length()));
        } else {
            //In tutti i casi diversi da redirect e blocco, passo 
            //il controllo al valve successivo della catena, se esiste.
            Valve nextValve = getNext();
            if (nextValve!=null) 
                nextValve.invoke(request, response);
        }
    }
}

Per compilare la classe, includere nel "classPath" i seguenti jar (l'ultimo necessario per il logging):
  • Apache Tomcat 8.x.y/lib/*
  • Apache Tomcat 8.x.y/bin/tomcat-juli.jar
Non resta che compilare, inserire in un "jar" e mettere il jar creato nella cartella "lib" di tomcat.
Una volta riavviato tomcat, accedendo per esempio con il browser "Edge" verra' scritto un log del tipo:
20-Nov-2016 02:49:32.200 INFO [...] org.giansante.tomcat.test.ExampleValve.matchPattern Trovato Agent proibito (PATTERN=[agent2]; EXPR=[.*edge.*])

Concludendo, la classe creata e' molto semplice e serve solo a mostrare una piccolissima parte di cosa si puo' fare con i valve, ma le possibilita' sono praticamente infinite.

Riferimenti

  1. The Valve Component
    https://tomcat.apache.org/tomcat-8.0-doc/config/valve.html

  2. Class ValveBase (API doc)
    https://tomcat.apache.org/tomcat-8.0-doc/api/org/apache/catalina/valves/ValveBase.html
  3. Class Request (API doc)
    https://tomcat.apache.org/tomcat-8.0-doc/api/org/apache/catalina/connector/Request.html
  4. Class Response (API doc)
    https://tomcat.apache.org/tomcat-8.0-doc/api/org/apache/catalina/connector/Response.html
  5. Interface Container (API doc)
    https://tomcat.apache.org/tomcat-8.0-doc/api/org/apache/catalina/Container.html
  6. Interface Context (API doc)
    https://tomcat.apache.org/tomcat-8.0-doc/api/org/apache/catalina/Context.html
  7. Interface Engine (API doc)
    https://tomcat.apache.org/tomcat-8.0-doc/api/org/apache/catalina/Engine.html
  8. Interface Host (API doc)
    https://tomcat.apache.org/tomcat-8.0-doc/api/org/apache/catalina/Host.html

Hai trovato utile questo articolo?
Aiutami a condividerlo o metti un "mi piace".
Grazie mille!


Gli strumenti di condivisione (Google+, Facebook) sono visibili in alto a destra solo dopo aver accettato la policy di utilizzo dei cookie per questo sito.
FAQ - Come faccio a cambiare la mia scelta?

 

Strumenti (myjsp.feelinglinux.com)
Gioco: allenamento con la tastiera Strumenti di codifica/decodifica URI (%-encoding) e Base64 Strumenti di calcolo online per IP e Reti
QUIZ GAME
Quiz game

Cerca @myjsp.feelinglinux.com

Pubblicita'