我能从<filter-mapping>中排除一些具体的URL吗?

139

我希望对所有URL应用一些具体的过滤器,除了一个具体的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>
8个回答

172

标准Servlet API不支持此功能。您可能希望使用一个重写URL过滤器,如Tuckey的(它与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");
}

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

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
另请参阅:

3
我的问题是筛选器不是我自己的,而是来自组件库。 - Roman
4
您需要使用组件库过滤器并进行扩展,以添加您想要使用的代码以执行排除操作。 - gbtimmon
@BalusC 如果“/specialpath”只提供像js、css等静态资源,那么chain.doFilter()会使响应变慢吗?有没有一种方法可以直接提供资源而不需要链接过滤器? - BenhurCD
@BenhurCD:我真的不知道你是怎么想到这个性能问题的。 - BalusC

15

我采用了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);
    }
}

12

当您想要避免特定过滤器及其后续过滤器时,此方法是有效的。如果您希望将某些内容作为静态资源在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");
    }
}

很不幸,如果你只想跳过过滤器链中的单个步骤,同时保留后续步骤,这是行不通的。


我尝试使用您的解决方案,但对于我应用过滤器并打破链条的文件,我遇到了以下错误:Static Resource Filter抛出未捕获的异常:java.io.FileNotFoundException。有任何想法为什么会这样? - shamaleyte
1
在多上下文设置中,使用.getRequestURI()会出现错误(很可能导致404),因为.getRequestDispatcher相对于上下文路径进行解析。如果您的上下文路径是/a,那么在您的示例中,请求URI将是/a/static,并且使用getRequestDispatcher("/a/static")将导致其相对于/a/a/static进行解析。修复方法:使用.getServletPath()代替.getRequestURI()。我将提交一个编辑来修复此问题,但只是想留下评论FYI。 - Reid

3
我认为你不能这样做,唯一的另一种配置选择是列举想要被过滤的路径。所以,你可以添加一些像/this/*/that/*等的路径,但当你有很多这样的路径时,这不会导致一个足够的解决方案。
你可以做的是在过滤器中添加一个参数,提供一个表达式(如正则表达式),用于跳过匹配到路径的过滤功能。虽然Servlet容器仍将针对这些URL调用您的过滤器,但您将更好地控制其配置。
编辑:既然你说你无法控制过滤器,那么你可以继承该过滤器,在其方法中调用super方法,除非存在你想要跳过的url路径,并像@BalusC建议的那样遵循过滤器链,或构建一个过滤器,该过滤器实例化您的过滤器并在相同情况下委托。在两种情况下,过滤器参数都将包括您添加的表达式参数以及您从中继承或委托的过滤器的参数。
构建代理过滤器(包装器)的优点是,您可以将包装过滤器的过滤器类作为参数添加,并在其他情况下重用它。

3

我还需要在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来说应该是非常常见的情况!


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

  • 拦截要绕过的路径
  • 跳转并执行过滤器链的最后一个环节(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();
              }           
          }   
      }

0

我遇到了同样的问题,但我找到了下面的答案。

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

这样你就不必麻烦具体的Filter类了。


0

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接