我是否可以从 < url-pattern > inside < filter-Mapping > 中排除一些具体的 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>
212479 次浏览

标准的 Servlet API 不支持这种工具。您可能希望使用像 塔基是其中之一这样的 rewrite-URL 过滤器(与 Apache HTTPD 的 mod_rewrite非常相似) ,或者在监听 /*的 Filter 的 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上让第三方过滤器侦听(分派)。

参见:

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

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

剪辑

既然你提到你不能控制这个过滤器,你可以继承这个过滤器,在它的方法中调用 super方法,除非你想跳过的 url 路径出现,然后像@BalusC 建议的那样跟随过滤器链,或者构建一个过滤器,在相同的情况下实例化你的过滤器和委托。在这两种情况下,筛选器参数都将包括您添加的表达式参数和从其继承或委托的筛选器参数。

构建委托过滤器(包装器)的优点是,您可以添加包装过滤器的过滤器类作为参数,并在其他情况下重用它,如本例所示。

我使用了一种方法 由 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);
}
}

当您希望防止某个筛选器和以下所有筛选器时,此方法可以工作。如果您希望将某些内容作为静态资源提供给 servlet 容器,而不是让应用程序逻辑(通过 GuiceFilter 这样的过滤器) ,那么它应该能够很好地工作:

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

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>

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 代码中的 URL 模式(/{ servicename }/api/stats/)进行过滤。

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

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

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

Xml

 <!-- set this param value for the filter-->
<init-param>
<param-name>freePages</param-name>
<param-value>
MainFrame.jsp;
</param-value>
</init-param>

Java

strFreePages = config.getInitParameter("freePages"); //get the exclue pattern from config file
isFreePage(strRequestPage)  //decide the exclude path

这样,您就不必骚扰具体的 Filter 类。

如果由于某种原因您不能更改原始的过滤器映射(在我的例子中是“/*”) ,并且您正在分派到一个不可更改的第三方过滤器,您可以发现以下内容非常有用:

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

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