¿Puedo excluir algunas URL concretas de <url-pattern> dentro de <filter-mapping>?

127

Quiero que se aplique un filtro de concreto para todas las URL excepto para un concreto (es decir, /*excepto para /specialpath).

¿Existe la posibilidad de hacer eso?


Código de muestra:

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

Respuestas:

156

La API de Servlet estándar no es compatible con esta instalación. Es posible que desee utilizar un filtro de reescritura de URL para esto, como el de Tuckey (que es muy similar al de Apache HTTPD mod_rewrite), o agregar una verificación en el doFilter()método de escucha del filtro /*.

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

Si es necesario, puede especificar las rutas a ignorar como parte init-paramdel filtro para poder controlarlo de web.xmltodos modos. Puede obtenerlo en el filtro de la siguiente manera:

private String pathToBeIgnored;

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

Si el filtro es parte de la API de un tercero y, por lo tanto, no puede modificarlo, entonces asigne un mapa más específico url-pattern, por ejemplo, /otherfilterpath/*y cree un nuevo filtro /*que reenvíe a la ruta que coincida con el filtro de un tercero.

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

Para evitar que este filtro se llame a sí mismo en un bucle infinito, debe dejarlo escuchar (despachar) REQUESTsolo y el filtro de terceros FORWARDsolo.

Ver también:

BalusC
fuente
3
Mi problema es que el filtro no es mío, es de la biblioteca de componentes.
Romano
44
Ypu debe tomar el filtro de la biblioteca de componentes y extenderlo para agregar el código que desea usar para realizar las exclusiones.
gbtimmon
@BalusC Si el "/ specialpath" solo sirve un recurso estático como js, ​​css, etc., ¿chain.doFilter () hace que la respuesta sea más lenta? ¿Hay algún método para servir el recurso directamente sin encadenar el filtro?
BenhurCD
@BenhurCD: Realmente no tengo idea de cómo podrías llegar a este problema de rendimiento.
BalusC
13

Utilicé un enfoque descrito por Eric Daugherty : creé un servlet especial que siempre responde con el código 403 y puse su mapeo antes que el general.

Fragmento de mapeo:

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

Y la clase de 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
fuente
9

Este enfoque funciona cuando desea evitar cierto filtro y todos los siguientes. Debería funcionar bien si, por ejemplo. desea servir algún contenido como recursos estáticos dentro de su contenedor de servlet en lugar de permitir la lógica de su aplicación (a través de un filtro como GuiceFilter):

Asigne la carpeta con sus archivos de recursos estáticos al servlet predeterminado. Cree un filtro de servlet y póngalo antes del GuiceFilter en su web.xml. En su filtro creado, puede separarse entre reenviar algunas solicitudes al GuiceFilter y otras directamente al despachador. El siguiente ejemplo ...

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

Desafortunadamente, si solo desea omitir un solo paso en la cadena de filtros mientras mantiene los siguientes, esto no funcionará.

kvitso
fuente
Intenté ir con su solución, pero para los archivos que aplico el filtro y rompo la cadena, aparece el siguiente error; Excepción no detectada generada por el filtro Filtro de recursos estáticos: java.io.FileNotFoundException. ¿Alguna idea de por qué?
shamaleyte
En configuraciones de contexto múltiple, el uso .getRequestURI()se interrumpirá (lo que probablemente causará un 404) porque se .getRequestDispatcherresuelve en relación con la ruta de contexto . Si su ruta de contexto es /a, entonces, en su ejemplo, el URI de la solicitud sería /a/static, y el uso getRequestDispatcher("/a/static")hará que se resuelva en su /a/a/staticlugar. Solución: use en .getServletPath()lugar de .getRequestURI(). Enviaré una edición para arreglar esto, pero solo quería dejar un comentario FYI
Reid
3

No creo que pueda, la única otra alternativa de configuración es enumerar las rutas que desea filtrar, por lo que en lugar de /*poder agregar algunas para /this/*y /that/*etc., pero eso no conducirá a una solución suficiente cuando tenga mucho de esos caminos

Lo que puede hacer es agregar un parámetro al filtro que proporcione una expresión (como una expresión regular) que se utiliza para omitir la funcionalidad del filtro para las rutas coincidentes. El contenedor de servlet aún llamará a su filtro para esas URL, pero tendrá un mejor control sobre la configuración.

Editar

Ahora que mencionas que no tienes control sobre el filtro, lo que puedes hacer es heredar de ese supermétodo los métodos de llamada en sus métodos, excepto cuando la ruta de URL que quieres omitir está presente y seguir la cadena de filtros como @BalusC propuso, o construir un filtro que crea instancias de su filtro y delega en las mismas circunstancias. En ambos casos, los parámetros del filtro incluirían tanto el parámetro de expresión que agregue como los del filtro del que herede o delegue.

La ventaja de crear un filtro de delegación (un contenedor) es que puede agregar la clase de filtro del filtro ajustado como parámetro y reutilizarlo en otras situaciones como esta.

rsp
fuente
3

También tuve que filtrar según el patrón de URL (/ {servicename} / api / stats /) en el código de Java.

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

Pero es extraño, ese servlet no admite un patrón de URL que no sea (/ *). ¡Este debería ser un caso muy común para las API de servlet!

Sindhu
fuente
0

Me he encontrado con el mismo problema, pero encuentro una respuesta que se muestra a continuación.

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 esta manera no tiene que acosar a la clase de filtro concreto.

nelson
fuente
0

Si por alguna razón no puede cambiar la asignación del filtro original ("/ *" en mi caso) y está enviando a un filtro de terceros inmutable, puede encontrar útil lo siguiente:

  • Interceptar el camino a ser ignorado
  • Salte y ejecute el último anillo de la cadena de filtro (el servlet mismo)
  • El salto se realiza mediante reflexión, inspeccionando las instancias del contenedor en modo de depuración

Los siguientes trabajos en 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
fuente