Kann ich einige konkrete URLs von <url-pattern> in <filter-mapping> ausschließen?

127

Ich möchte, dass ein konkreter Filter für alle URLs mit Ausnahme eines konkreten (dh für /*außer für /specialpath) angewendet wird .

Gibt es eine Möglichkeit das zu tun?


Beispielcode:

<filter>
    <filter-name>SomeFilter</filter-name>
    <filter-class>org.somproject.AFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>SomeFilter</filter-name>
    <url-pattern>/*</url-pattern>   <!-- the question is: how to modify this line?  -->
    <dispatcher>REQUEST</dispatcher>
    <dispatcher>FORWARD</dispatcher>
</filter-mapping>
römisch
quelle

Antworten:

156

Die Standard-Servlet-API unterstützt diese Funktion nicht. Möglicherweise möchten Sie entweder einen Umschreib-URL-Filter wie den von Tuckey (der Apache-HTTPDs sehr ähnlich ist mod_rewrite) verwenden oder die doFilter()Methode des Filter-Abhörens überprüfen /*.

String path = ((HttpServletRequest) request).getRequestURI();
if (path.startsWith("/specialpath/")) {
    chain.doFilter(request, response); // Just continue chain.
} else {
    // Do your business stuff here for all paths other than /specialpath.
}

Sie können bei Bedarf die zu ignorierenden Pfade als init-paramFilter angeben , damit Sie sie web.xmltrotzdem steuern können. Sie können es wie folgt in den Filter einfügen:

private String pathToBeIgnored;

public void init(FilterConfig config) {
    pathToBeIgnored = config.getInitParameter("pathToBeIgnored");
}

Wenn der Filter Teil der Drittanbieter-API ist und Sie ihn daher nicht ändern können, ordnen Sie ihn einem spezifischeren zu url-pattern, z. B. /otherfilterpath/*und erstellen Sie einen neuen Filter, /*der an den Pfad weitergeleitet wird, der dem Drittanbieterfilter entspricht.

String path = ((HttpServletRequest) request).getRequestURI();
if (path.startsWith("/specialpath/")) {
    chain.doFilter(request, response); // Just continue chain.
} else {
    request.getRequestDispatcher("/otherfilterpath" + path).forward(request, response);
}

Um zu vermeiden, dass sich dieser Filter in einer Endlosschleife aufruft, müssen Sie ihn nur abhören (versenden) und REQUESTnur den Filter eines Drittanbieters einschalten lassen FORWARD.

Siehe auch:

BalusC
quelle
3
Mein Problem ist, dass der Filter nicht von mir stammt, sondern aus der Komponentenbibliothek stammt.
Roman
4
Ypu sollte den Filter der Komponentenbibliothek verwenden und erweitern, um den Code hinzuzufügen, den Sie zum Ausführen der Ausschlüsse verwenden möchten.
Gbtimmon
@BalusC Wenn der "/ specialpath" nur eine statische Ressource wie js, css usw. bereitstellt, verlangsamt chain.doFilter () die Antwort? Gibt es eine Methode, um die Ressource direkt bereitzustellen, ohne den Filter zu verketten?
BenhurCD
@BenhurCD: Ich habe wirklich keine Ahnung, wie Sie jemals zu diesem Leistungsproblem kommen könnten.
BalusC
13

Ich habe einen von Eric Daugherty beschriebenen Ansatz verwendet : Ich habe ein spezielles Servlet erstellt, das immer mit 403-Code antwortet und dessen Zuordnung vor das allgemeine stellt.

Mapping-Fragment:

  <servlet>
    <servlet-name>generalServlet</servlet-name>
    <servlet-class>project.servlet.GeneralServlet</servlet-class>
  </servlet>
 <servlet>
    <servlet-name>specialServlet</servlet-name>
    <servlet-class>project.servlet.SpecialServlet</servlet-class>
 </servlet>
 <servlet-mapping>
    <servlet-name>specialServlet</servlet-name>
    <url-pattern>/resources/restricted/*</url-pattern>
 </servlet-mapping>
 <servlet-mapping>
    <servlet-name>generalServlet</servlet-name>
    <url-pattern>/resources/*</url-pattern>
 </servlet-mapping>

Und die Servlet-Klasse:

public class SpecialServlet extends HttpServlet {
    public SpecialServlet() {
        super();
    }
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.sendError(HttpServletResponse.SC_FORBIDDEN);
    }
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.sendError(HttpServletResponse.SC_FORBIDDEN);
    }
}
mrzasa
quelle
9

Dieser Ansatz funktioniert, wenn Sie einen bestimmten Filter und alle folgenden verhindern möchten. Es sollte gut funktionieren, wenn Sie z. Sie möchten einige Inhalte als statische Ressourcen in Ihrem Servlet-Container bereitstellen, anstatt Ihre Anwendungslogik zuzulassen (über einen Filter wie GuiceFilter):

Ordnen Sie den Ordner mit Ihren statischen Ressourcendateien dem Standardservlet zu. Erstellen Sie einen Servlet-Filter und platzieren Sie ihn vor dem GuiceFilter in Ihrer web.xml. In Ihrem erstellten Filter können Sie zwischen der Weiterleitung einiger Anforderungen an den GuiceFilter und anderen direkt an den Dispatcher unterscheiden. Beispiel folgt ...

web.xml

<servlet-mapping>
    <servlet-name>default</servlet-name>
    <url-pattern>/static/*</url-pattern>
</servlet-mapping>

<filter>
    <filter-name>StaticResourceFilter</filter-name>
    <filter-class>com.project.filter.StaticResourceFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>StaticResourceFilter</filter-name>
    <url-pattern>/static/*</url-pattern>
</filter-mapping>

<filter>
    <filter-name>guiceFilter</filter-name>
    <filter-class>com.google.inject.servlet.GuiceFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>guiceFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

StaticResourceFilter.class

public class StaticResourceFilter implements Filter {

    private final static Logger LOGGER = LoggerFactory.getLogger(StaticResourceFilter.class);

    private static final String RESOURCE_PATH = "/static/";
    @Override
    public void init(final FilterConfig filterConfig) throws ServletException {
        LOGGER.info("StaticResourceFilter initialized");
    }

    @Override
    public void doFilter(final ServletRequest request, final ServletResponse response,
                         final FilterChain chain) throws IOException, ServletException {

        String path = ((HttpServletRequest) request).getServletPath();
        if (path.toLowerCase().startsWith(RESOURCE_PATH)) {
            request.getRequestDispatcher(path).forward(request, response);
        } else {
            chain.doFilter(request, response);
        }
    }

    @Override
    public void destroy() {
        LOGGER.info("StaticResourceFilter destroyed");
    }
}

Leider funktioniert dies nicht, wenn Sie nur einen einzelnen Schritt in der Filterkette überspringen möchten, während Sie die folgenden beibehalten.

kvitso
quelle
Ich habe versucht, mit Ihrer Lösung zu arbeiten, aber für die Dateien, bei denen ich den Filter anwende und die Kette unterbreche, wird der folgende Fehler angezeigt. Vom Filter ausgelöste nicht erfasste Ausnahme Filter für statische Ressourcen: java.io.FileNotFoundException. Irgendeine Idee warum?
Shamaleyte
In Setups mit mehreren Kontexten wird die Verwendung .getRequestURI()unterbrochen (was höchstwahrscheinlich einen 404 verursacht), da .getRequestDispatcherdie Auflösung relativ zum Kontextpfad erfolgt . Wenn Ihr Kontextpfad lautet /a, lautet der Anforderungs-URI in Ihrem Beispiel. Wenn Sie ihn /a/staticverwenden, getRequestDispatcher("/a/static")wird er /a/a/staticstattdessen aufgelöst. Fix: .getServletPath()anstelle von verwenden .getRequestURI(). Ich werde eine Bearbeitung einreichen, um dies zu beheben, wollte aber nur einen Kommentar hinterlassen FYI
Reid
3

Ich glaube nicht, dass Sie dies können. Die einzige andere Konfigurationsalternative besteht darin, die Pfade aufzulisten, die gefiltert werden sollen, sodass Sie stattdessen /*einige für /this/*und /that/*usw. hinzufügen können. Dies führt jedoch nicht zu einer ausreichenden Lösung, wenn Sie viel haben dieser Wege.

Sie können dem Filter einen Parameter hinzufügen, der einen Ausdruck (wie einen regulären Ausdruck) bereitstellt, mit dem die Filterfunktionalität für die übereinstimmenden Pfade übersprungen wird. Der Servlet-Container ruft weiterhin Ihren Filter für diese URLs auf, aber Sie haben eine bessere Kontrolle über die Konfiguration.

Bearbeiten

Nachdem Sie nun erwähnt haben, dass Sie keine Kontrolle über den Filter haben, können Sie entweder von diesen Filteraufrufmethoden superin ihren Methoden erben, es sei denn, der URL-Pfad, den Sie überspringen möchten, ist vorhanden, und Sie folgen der Filterkette wie von @BalusC vorgeschlagen oder erstellen Ein Filter, der Ihren Filter instanziiert und unter den gleichen Umständen delegiert. In beiden Fällen umfassen die Filterparameter sowohl den von Ihnen hinzugefügten Ausdrucksparameter als auch die des Filters, von dem Sie erben oder an den Sie delegieren.

Der Vorteil des Erstellens eines delegierenden Filters (eines Wrappers) besteht darin, dass Sie die Filterklasse des umschlossenen Filters als Parameter hinzufügen und in anderen Situationen wie dieser wiederverwenden können.

rsp
quelle
3

Ich musste auch basierend auf dem URL-Muster (/ {Servicename} / api / stats /) im Java-Code filtern.

if (path.startsWith("/{servicename}/api/statistics/")) {
validatingAuthToken(((HttpServletRequest) request).getHeader("auth_token"));
filterChain.doFilter(request, response);            
}

Aber es ist bizarr, dass das Servlet kein anderes URL-Muster als (/ *) unterstützt. Dies sollte ein sehr häufiger Fall für Servlet-APIs sein!

Sindhu
quelle
0

Ich bin auf dasselbe Problem gestoßen, aber ich finde unten eine Antwort.

web.xml

 <!-- set this param value for the filter-->
    <init-param>
            <param-name>freePages</param-name>
            <param-value>
            MainFrame.jsp;
            </param-value>
    </init-param>

filter.java

strFreePages = config.getInitParameter("freePages"); //get the exclue pattern from config file
isFreePage(strRequestPage)  //decide the exclude path

Auf diese Weise müssen Sie die konkrete Filterklasse nicht belästigen.

Nelson
quelle
0

Wenn Sie aus irgendeinem Grund die ursprüngliche Filterzuordnung nicht ändern können (in meinem Fall "/ *") und an einen unveränderlichen Filter eines Drittanbieters senden, finden Sie Folgendes nützlich:

  • Fangen Sie den zu umgehenden Pfad ab
  • Zum letzten Ring der Filterkette springen und diesen ausführen (das Servlet selbst)
  • Das Überspringen erfolgt durch Reflektion, wobei die Containerinstanzen im Debug-Modus überprüft werden

Folgendes funktioniert in Weblogic 12.1.3:

      import org.apache.commons.lang3.reflect.FieldUtils;
      import javax.servlet.Filter;

      [...]

      @Override   
      public void doFilter(ServletRequest request, ServletRespons response, FilterChain chain) throws IOException, ServletException { 
          String path = ((HttpServletRequest) request).getRequestURI();

          if(!bypassSWA(path)){
              swpFilterHandler.doFilter(request, response, chain);

          } else {
              try {
                  ((Filter) (FieldUtils.readField(
                                (FieldUtils.readField(
                                        (FieldUtils.readField(chain, "filters", true)), "last", true)), "item", true)))
                  .doFilter(request, response, chain);
              } catch (IllegalAccessException e) {
                  e.printStackTrace();
              }           
          }   
      }
robermann
quelle