1、Filter的目的
Filter用于在Servlet之前检测和修改请求和响应,它可以拒绝、重定向或转发请求。常见的有这几种:
- 日志过滤器
使用过滤器记录请求,提供请求日志记录,还可以添加追踪信息用于特定的请求。
- 验证过滤器
用于验证用户已经“登陆”,很多Servlet的操作都需要验证用户的身份权限,验证过滤器就是把这一功能提取出来,将验证和授权操作集中带一个位置,方便开发和维护
- 压缩和加密过滤器
其实根本上的目的和上一个验证过滤器一样,不过这里的功能是压缩和加密
- 错误处理过滤器
对于客户端来说,有时候出现错误错误通常会返回一个Http响应代码500,一般还伴随这一些诊断信息,通常这些信息对于开发者而言是有用的,但是对于黑客来说,这些诊断信息可能暴露一些敏感的系统信息,而且错误几乎是不可避免的,所以我们需要防止这些信息泄露,使用错误处理过滤器为用户显示一个特殊的页面,比如这个,url是我随便填的一个:
2、创建Filter
其实Filter只是一个接口,源代码如下:
public interface Filter { void init(FilterConfig var1) throws ServletException; void doFilter(ServletRequest var1, ServletResponse var2, FilterChain var3) throws IOException, ServletException; void destroy(); }
- init()方法用于初始化过滤器,这里可以看到一个FilterConfig类,Filter就是使用这个类来进行初始化配置
- doFilter()方法就是用来实现具体功能,检测和修改请求和响应,因为一个项目可能不止需要一个Filter,就比如某个Servlet需要先验证再加密压缩,可能就需要使用两个Filter,这样就形成了一个过滤器链,验证的Filter成功后再将请求传递给加密压缩的Filter,FilterChain将用于传递请求和响应。
- destroy()方法将用于销毁Filter
3、Filter链
通常只有一个Servlet可以处理请求,但可以使用许多过滤拦截请求。一个请求将被很多过滤器处理后传递最终传递给Servlet。如图所示这样:
这样的工作方式被称为过滤器链,这种工作方式相当于一个工作栈,当请求进入时,第一个过滤器将被添第一个过滤器将被添加到栈中,然后再把第二个过滤器添加到栈中,直到最终的Servlet,这是栈的最后一个元素,当Servlet的请求完成的时候,Servlet将从栈中去除,然后控制权将返回最后一个过滤器,然后再移除最后一个过滤器,一直到第一个过滤器中,当栈空的时候,请求处理也就完成了。
4、Filter的部署方式
Filter除了可以映射到某个URL上,某个Servlet上,还可以映射到不同的请求派发器类型,在Servlet容器中有这么几种派发请求方式:
- 普通请求:通常来自客户端,,并包含了容器中特定的Web应用程序的目标URL
- 转发请求:当代码调用RequestDispatcher的forward方法或者使用<jsp:forward>标签是将触发这些请求。
- 包含请求:使用<jsp:include>标签或者调用RequestDispatcher的include方法的时候,将会产生一个不同的、与原始请求相关的内部请求
- 错误资源请求:访问HTTP错误的错误页面的请求
- 异步请求
部署Filter有两种方式,第一种是使用部署描述符
<filter> <filter-name>filterA</filter-name> <filter-class>filter.FilterA</filter-class> </filter> <filter-mapping> <filter-name>filterA</filter-name> <url-pattern>/filterA</url-pattern> <servlet-name>someServlet</servlet-name> <dispatcher>REQUEST</dispatcher> </filter-mapping>
- <filter-name>将指定filter的名字
- <filter-class>将指定filter的类名
- <url-pattern>将指定映射的URL
- <servlet-name>将指定映射的Servlet的名字
- <dispatcher>将响应的请求的派发方式,有效的<dispatcher>类型有REQUEST、FORWARD、INCLUDE、ERROR、ASYNC
使用注解:
@WebFilter( filterName = "filterA", urlPatterns = {"/filterA","/filterB"}, servletNames = {"someServlet"}, dispatcherTypes = {DispatcherType.REQUEST,DispatcherType.ASYNC} )
5、过滤器排序
- 匹配请求的过滤器将按照它们出现在部署描述符的顺序添加到过滤器链中
- URL映射的过滤器优先级比Servlet名称映射的过滤器优先级高
定义三个Filter,分别为FilterA、FilterB、FilterC,三个Filter在启动的时候都会输出类名+start,然后将请求和响应传递给下一个Filter,到出栈之前都会输出类名+end
public class FilterA implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { System.out.println("FilterA start"); filterChain.doFilter(servletRequest,servletResponse); System.out.println("FilterA end"); } @Override public void destroy() { } }
然后定义一个Servlet,这里就是启动的时候输出“ServletOne.doget() start”,结束的时候输出“ServletOne.doget() start”
@WebServlet( name = "servletOne", urlPatterns = "/servletOne" ) public class ServletOne extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("ServletOne.doget() start"); resp.getWriter().write("ServletOne"); System.out.println("ServletOne.doget() end"); } }
然后这样配置Filter:
<filter> <filter-name>filterA</filter-name> <filter-class>filter.FilterA</filter-class> </filter> <filter-mapping> <filter-name>filterA</filter-name> <servlet-name>servletOne</servlet-name> </filter-mapping> <filter> <filter-name>filterB</filter-name> <filter-class>filter.FilterB</filter-class> </filter> <filter-mapping> <filter-name>filterB</filter-name> <url-pattern>/servletOne</url-pattern> </filter-mapping> <filter> <filter-name>filterC</filter-name> <filter-class>filter.FilterC</filter-class> </filter> <filter-mapping> <filter-name>filterC</filter-name> <url-pattern>/servletOne</url-pattern> </filter-mapping>
FilterA将映射到名称为servletOne的Servlet,FilterB、FilterC都将映射的URL是/servletOne。运行成功之后在浏览器中输入http://localhost:8080/hello-world/servletOne,我们将在IDEA的输出中看到这样的结果
filter.FilterB@17f58a6f start filter.FilterC@193266e1 start filter.FilterA@1d0de11fstart ServletOne.doget() start ServletOne.doget() end filter.FilterA@1d0de11f end filter.FilterC@193266e1 end filter.FilterB@17f58a6f end
可以看到最先启动的是FilterB,其次是FilterC,最后是FilterA。因为FilterA是映射的Servlet的名称而FilterB、FilterC都是映射的URL所以FilterA将在最后启动,同时也在Servlet结束后结束。因为FilterB在部署时在FilterC之前所以FilterB是最先被启动的,最后结束的。