Posso excluir alguns URLs concretos de <url-pattern> dentro de <filter-mapping>?

127

Quero que um filtro de concreto seja aplicado a todos os URLs, exceto um concreto (ou seja, /*exceto /specialpath).

Existe a possibilidade de fazer isso?


Código de amostra:

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

Respostas:

156

A API Servlet padrão não suporta esse recurso. Você pode usar um filtro de URL de reescrita como o de Tuckey (que é muito semelhante ao HTTPD do Apache mod_rewrite) ou adicionar uma verificação no doFilter()método de escuta do 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.
}

Se necessário, é possível especificar os caminhos a serem ignorados como um init-paramfiltro, para que você possa controlá-lo da mesma web.xmlmaneira. Você pode obtê-lo no filtro da seguinte maneira:

private String pathToBeIgnored;

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

Se o filtro fizer parte da API de terceiros e, portanto, você não puder modificá-lo, mapeie-o de forma mais específica url-pattern, por exemplo, /otherfilterpath/*e crie um novo filtro no /*qual encaminha para o caminho correspondente ao filtro de terceiros.

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 esse filtro se auto-identifique em um loop infinito, é necessário deixá-lo ouvir (despachar) REQUESTapenas e o filtro de terceiros FORWARDsomente.

Veja também:

BalusC
fonte
3
Meu problema é que o filtro não é meu, é da biblioteca de componentes.
Roman
4
O Ypu deve pegar o filtro da biblioteca do componente e estendê-lo para adicionar o código que você deseja usar para executar as exclusões.
Gbtimmon
@BalusC Se o "/ specialpath" serve apenas um recurso estático como js, ​​css, etc, chain.doFilter () torna a resposta mais lenta? Existe um método para servir o recurso diretamente sem encadear o filtro?
BenhurCD
@BenhurCD: Eu realmente não tenho ideia de como você poderia chegar a essa preocupação com o desempenho.
BalusC
13

Usei uma abordagem descrita por Eric Daugherty : criei um servlet especial que sempre responde com o código 403 e coloca seu mapeamento antes do geral.

Fragmento de mapeamento:

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

E a classe 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
fonte
9

Essa abordagem funciona quando você deseja impedir um determinado filtro e todos os seguintes. Deve funcionar bem se você, por exemplo. deseja veicular algum conteúdo como recursos estáticos no contêiner do servlet em vez de deixar a lógica do aplicativo (por meio de um filtro como o GuiceFilter):

Mapeie a pasta com seus arquivos de recursos estáticos para o servlet padrão. Crie um filtro de servlet e coloque-o antes do GuiceFilter no seu web.xml. No filtro criado, você pode separar entre encaminhar algumas solicitações para o GuiceFilter e outras diretamente para o despachante. Exemplo a seguir ...

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

Infelizmente, se você quiser pular uma única etapa da cadeia de filtros, mantendo as seguintes, isso não funcionará.

kvitso
fonte
Tentei ir com sua solução, mas para os arquivos que aplico o filtro e quebro a cadeia, recebo o seguinte erro; Exceção não capturada lançada pelo filtro Static Resource Filter: java.io.FileNotFoundException. Alguma idéia do porquê?
Shamaleyte
Nas configurações de múltiplos contextos, o uso .getRequestURI()será interrompido (provavelmente causando um erro 404) porque é .getRequestDispatcherresolvido em relação ao caminho do contexto . Se o seu caminho de contexto for /a, então, no seu exemplo, o URI da solicitação seria /a/statice o uso getRequestDispatcher("/a/static")fará com que ele seja resolvido /a/a/static. Correção: use em .getServletPath()vez de .getRequestURI(). Vou enviar uma edição para corrigir isso, mas só queria deixar um comentário FYI
Reid
3

Eu não acho que você pode, a única outra alternativa de configuração é enumerar os caminhos que você deseja filtrar, portanto, em vez de /*adicionar alguns para /this/*e /that/*etc, mas isso não levará a uma solução suficiente quando você tiver muito desses caminhos.

O que você pode fazer é adicionar um parâmetro ao filtro, fornecendo uma expressão (como uma expressão regular) que é usada para ignorar a funcionalidade do filtro para os caminhos correspondentes. O contêiner de servlet ainda chamará seu filtro para esses URLs, mas você terá melhor controle sobre a configuração.

Editar

Agora que você mencionou que não tem controle sobre o filtro, o que você pode fazer é herdar os supermétodos de chamada de filtro em seus métodos, exceto quando o caminho da URL que você deseja pular estiver presente e seguir a cadeia de filtros como @BalusC proposta, ou compilar um filtro que instancia seu filtro e delega nas mesmas circunstâncias. Nos dois casos, os parâmetros do filtro incluem o parâmetro de expressão que você adiciona e os do filtro que você herda ou delega.

A vantagem de criar um filtro de delegação (um invólucro) é que você pode adicionar a classe de filtro do filtro empacotado como parâmetro e reutilizá-lo em outras situações como esta.

rsp
fonte
3

Eu também tive que filtrar com base no padrão de URL (/ {servicename} / api / stats /) no código java.

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

Mas é bizarro que o servlet não suporte padrão de URL diferente de (/ *). Esse deve ser um caso muito comum para APIs de servlet!

Sindhu
fonte
0

Eu encontrei o mesmo problema, mas encontro uma resposta mostrando abaixo.

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

Dessa forma, você não precisa assediar a classe Filter concreta.

Nelson
fonte
0

Se, por qualquer motivo, você não puder alterar o mapeamento do filtro original ("/ *" no meu caso) e estiver enviando para um filtro de terceiros imutável, poderá achar útil o seguinte:

  • Intercepte o caminho a ser ignorado
  • Pule para e execute o último anel da cadeia de filtros (o próprio servlet)
  • O salto é feito via reflexão, inspecionando as instâncias do contêiner no modo de depuração

O seguinte funciona no 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
fonte