Puis-je exclure certaines URL concrètes de <url-pattern> dans <filter-mapping>?

127

Je veux qu'un filtre concret soit appliqué à toutes les URL sauf pour un béton (c'est-à-dire à l' /*exception de/specialpath ).

Y a-t-il une possibilité de le faire?


exemple de code:

<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>
romain
la source

Réponses:

156

L'API Servlet standard ne prend pas en charge cette fonctionnalité. Vous pouvez soit utiliser un filtre de réécriture d'URL pour cela comme celui de Tuckey (qui est très similaire à Apache HTTPD mod_rewrite), soit ajouter une vérification dans la doFilter()méthode d'écoute du filtre /*.

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

Vous pouvez si nécessaire spécifier les chemins à ignorer en tant que init-paramfiltre afin de pouvoir le contrôler de web.xmltoute façon. Vous pouvez l'obtenir dans le filtre comme suit:

private String pathToBeIgnored;

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

Si le filtre fait partie de l'API tierce et que vous ne pouvez donc pas le modifier, mappez-le sur un filtre plus spécifique url-pattern, par exemple, /otherfilterpath/*et créez un nouveau filtre sur /*lequel redirige vers le chemin correspondant au filtre tiers.

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

Pour éviter que ce filtre ne s'appelle lui-même dans une boucle infinie, vous devez le laisser écouter (répartir) REQUESTuniquement et le filtre tiers FORWARDuniquement.

Voir également:

BalusC
la source
3
Mon problème est que le filtre n'est pas le mien, il provient de la bibliothèque de composants.
Roman
4
Ypu doit prendre le filtre de la bibliothèque compnent et l'étendre afin d'ajouter le code que vous souhaitez utiliser pour effectuer les exclusions.
gbtimmon
@BalusC Si le "/ specialpath" ne sert qu'une ressource statique comme js, css etc, est-ce que chain.doFilter () ralentit la réponse? Existe-t-il une méthode pour servir la ressource directement sans chaîner le filtre?
BenhurCD
@BenhurCD: Je n'ai vraiment aucune idée de comment vous pourriez résoudre ce problème de performance.
BalusC
13

J'ai utilisé une approche décrite par Eric Daugherty : j'ai créé un servlet spécial qui répond toujours avec du code 403 et mis son mapping avant le général.

Fragment de cartographie:

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

Et la classe servlet:

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
la source
9

Cette approche fonctionne lorsque vous souhaitez empêcher un certain filtre et tous les suivants. Cela devrait bien fonctionner si vous par exemple. souhaitez diffuser du contenu en tant que ressources statiques dans votre conteneur de servlet au lieu de laisser la logique de votre application (via un filtre comme GuiceFilter):

Mappez le dossier avec vos fichiers de ressources statiques au servlet par défaut. Créez un filtre de servlet et placez-le avant le GuiceFilter dans votre web.xml. Dans votre filtre créé, vous pouvez séparer le transfert de certaines demandes vers GuiceFilter et d'autres directement vers le répartiteur. L'exemple suit ...

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

Malheureusement, si vous souhaitez simplement sauter une seule étape dans la chaîne de filtres tout en conservant celles qui suivent, cela ne fonctionnera pas.

kvitso
la source
J'ai essayé d'aller avec votre solution, mais pour les fichiers que j'applique le filtre et brise la chaîne, j'obtiens l'erreur suivante; Exception non interceptée lancée par le filtre Filtre de ressources statiques: java.io.FileNotFoundException. Une idée pourquoi?
shamaleyte
Dans les configurations multi-contextes, l'utilisation .getRequestURI()sera interrompue (provoquant très probablement un 404) car .getRequestDispatcherelle se résout par rapport au chemin du contexte . Si votre chemin de contexte est /a, alors dans votre exemple, l'URI de la requête serait /a/static, et l'utilisation le getRequestDispatcher("/a/static")fera résoudre à la /a/a/staticplace. Correction: utilisez à la .getServletPath()place de .getRequestURI(). Je vais soumettre une modification pour résoudre ce problème, mais je voulais juste laisser un commentaire FYI
Reid
3

Je ne pense pas que vous le puissiez, la seule autre alternative de configuration est d'énumérer les chemins que vous souhaitez filtrer, donc au lieu de /*vous pouvez en ajouter pour /this/*et /that/*etc., mais cela ne conduira pas à une solution suffisante lorsque vous en avez beaucoup de ces chemins.

Ce que vous pouvez faire est d'ajouter un paramètre au filtre en fournissant une expression (comme une expression régulière) qui est utilisée pour ignorer la fonctionnalité de filtre pour les chemins correspondants. Le conteneur de servlet appellera toujours votre filtre pour ces URL, mais vous aurez un meilleur contrôle sur la configuration.

Éditer

Maintenant que vous mentionnez que vous n'avez aucun contrôle sur le filtre, vous pouvez soit hériter de l'appel de ce filtre super méthodes d' dans ses méthodes, sauf lorsque le chemin d'url que vous souhaitez ignorer est présent et de suivre la chaîne de filtres comme @BalusC proposé, soit de construire un filtre qui instancie votre filtre et délègue dans les mêmes circonstances. Dans les deux cas, les paramètres de filtre incluent à la fois le paramètre d'expression que vous ajoutez et ceux du filtre dont vous héritez ou auquel vous déléguez.

L'avantage de créer un filtre délégant (un wrapper) est que vous pouvez ajouter la classe de filtre du filtre enveloppé en tant que paramètre et la réutiliser dans d'autres situations comme celle-ci.

rsp
la source
3

J'ai également dû filtrer en fonction du modèle d'URL (/ {servicename} / api / stats /) dans le code java.

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

Mais c'est bizarre, ce servlet ne prend pas en charge les modèles d'URL autres que (/ *), ce devrait être un cas très courant pour les API de servlet!

Sindhu
la source
0

J'ai rencontré le même problème, mais je trouve une réponse ci-dessous.

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

de cette façon, vous n'avez pas à harceler la classe Filter concrète.

Nelson
la source
0

Si, pour une raison quelconque, vous ne pouvez pas modifier le mappage de filtre d'origine ("/ *" dans mon cas) et que vous envoyez vers un filtre tiers non modifiable, vous pouvez trouver utile ce qui suit:

  • Intercepter le chemin à contourner
  • Passer à et exécuter le dernier anneau de la chaîne de filtres (le servlet lui-même)
  • Le saut se fait par réflexion, en inspectant les instances de conteneur en mode débogage

Les travaux suivants dans 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
la source