【问题标题】:Can I exclude some concrete urls from <url-pattern> inside <filter-mapping>?我可以从 <filter-mapping> 中的 <url-pattern> 中排除一些具体的 url 吗?
【发布时间】:2016-06-16 15:57:12
【问题描述】:

我希望对所有 url 应用一些具体的过滤器,除了一个具体的(即/* 除了/specialpath)。

有没有可能这样做?


示例代码:

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

【问题讨论】:

    标签: java servlets servlet-filters


    【解决方案1】:

    标准 Servlet API 不支持此功能。您可能希望为此使用重写 URL 过滤器,例如 Tuckey's one(与 Apache HTTPD 的 mod_rewrite 非常相似),或者在过滤器侦听 /*doFilter() 方法中添加一个检查。

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

    如有必要,您可以将要忽略的路径指定为过滤器的init-param,以便您可以在web.xml 中控制它。您可以按如下方式在过滤器中获取它:

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

    如果过滤器是第 3 方 API 的一部分,因此您无法修改它,则将其映射到更具体的 url-pattern,例如/otherfilterpath/* 并在 /* 上创建一个新过滤器,该过滤器转发到与第 3 方过滤器匹配的路径。

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

    为避免此过滤器在无限循环中调用自身,您需要让它仅在 REQUEST 上侦听(调度),而第三方过滤器仅在 FORWARD 上侦听。

    另见:

    【讨论】:

    • 我的问题是过滤器不是我的,它来自组件库。
    • Ypu 应该采用组件库过滤器并对其进行扩展,以便添加您要用于执行排除的代码。
    • @BalusC 如果“/specialpath”只提供静态资源,如 js、css 等,chain.doFilter() 是否会使响应变慢?有没有一种方法可以直接提供资源而无需链接过滤器?
    • @BenhurCD:我真的不知道你怎么会提出这个性能问题。
    【解决方案2】:

    我不认为你可以,唯一的其他配置选择是枚举你想要被过滤的路径,所以你可以为/this/*/that/* 等添加一些而不是/*,但是那当你有很多这些路径时,不会导致足够的解决方案。

    您可以做的是向过滤器添加一个参数,提供一个表达式(如正则表达式),该表达式用于跳过匹配路径的过滤器功能。 servlet 容器仍会为这些 url 调用您的过滤器,但您可以更好地控制配置。

    编辑

    既然您提到您无法控制过滤器,您可以做的是从该过滤器继承调用super 方法在其方法中,除非您要跳过的 url 路径存在并遵循过滤器链,如@BalusC 建议或构建一个过滤器,在相同情况下实例化您的过滤器和委托。在这两种情况下,过滤器参数都将包括您添加的表达式参数和您继承或委托给的过滤器的参数。

    构建委托过滤器(包装器)的优势在于,您可以将包装过滤器的过滤器类添加为参数,并在类似这种情况的其他情况下重用它。

    【讨论】:

      【解决方案3】:

      我使用了described by Eric Daugherty 的方法:我创建了一个特殊的 servlet,它总是以 403 代码回答并将其映射放在一般的之前。

      映射片段:

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

      还有 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);
          }
      }
      

      【讨论】:

        【解决方案4】:

        当您想要阻止某个过滤器和以下所有过滤器时,此方法有效。如果你例如它应该工作得很好。希望将某些内容作为 servlet 容器中的静态资源提供,而不是让您的应用程序逻辑(通过 GuiceFilter 之类的过滤器):

        将包含静态资源文件的文件夹映射到默认 servlet。创建一个 servlet 过滤器并将其放在 web.xml 中的 GuiceFilter 之前。在您创建的过滤器中,您可以将一些请求转发给 GuiceFilter 和其他请求直接转发给调度程序。示例如下...

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

        不幸的是,如果您只想跳过过滤器链中的一个步骤,同时保留后面的步骤,这将不起作用。

        【讨论】:

        • 我尝试使用您的解决方案,但是对于我应用过滤器并中断链的文件,我收到以下错误;过滤器静态资源过滤器抛出未捕获的异常:java.io.FileNotFoundException。知道为什么吗?
        • 在多上下文设置中,使用 .getRequestURI() 将中断(最有可能导致 404),因为 .getRequestDispatcher 解析相对于上下文路径。如果您的上下文路径是/a,那么在您的示例中,请求URI 将是/a/static,而使用getRequestDispatcher("/a/static") 将导致它解析为/a/a/static。修复:使用.getServletPath() 而不是.getRequestURI()。我将提交修改以解决此问题,但只是想发表评论仅供参考
        【解决方案5】:

        我还必须根据 java 代码中的 URL 模式(/{servicename}/api/stats/)进行过滤。

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

        但奇怪的是,servlet 不支持 (/*) 以外的 url 模式,这应该是 servlet API 的一个非常常见的情况!

        【讨论】:

          【解决方案6】:

          我也遇到过同样的问题,但我发现下面显示了一个答案。

          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
          

          这样您就不必骚扰具体的过滤器类。

          【讨论】:

            【解决方案7】:

            如果由于任何原因您无法更改原始过滤器映射(在我的情况下为“/*”)并且您正在分派到不可更改的第三方过滤器,您会发现以下有用:

            • 拦截要绕过的路径
            • 跳到并执行过滤器链的最后一个环(servlet 本身)
            • 跳过是通过反射完成的,在调试模式下检查容器实例

            以下工作在 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();
                          }           
                      }   
                  }
            

            【讨论】:

              【解决方案8】:

              我能够在 Spring 2 中按以下方式处理此问题

               private boolean isInPath(ServletRequest request) {
                 String PATH_TO_VALIDATE = "/path/";
                 String path = ((HttpServletRequest) request).getRequestURI();
                 return path != null && path.toLowerCase().contains(PATH_TO_VALIDATE);
              }
              

              【讨论】:

                猜你喜欢
                • 2016-11-07
                • 2011-09-27
                • 2011-05-27
                • 1970-01-01
                • 2011-04-11
                • 1970-01-01
                • 2013-10-06
                相关资源
                最近更新 更多