filter功能.它使用户可以改变一个 request和修改一个response. Filter 不是一个servlet,它不能产生一个response,它能够在一个request到达servlet之前预处理request,也可以在离开 servlet时处理response.换种说法,filter其实是一个”servlet chaining”(servlet 链).
一个filter 包括:
1. 在servlet被调用之前截获;
2. 在servlet被调用之前检查servlet request;
3. 根据需要修改request头和request数据;
4. 根据需要修改response头和response数据;
5. 在servlet被调用之后截获.
通俗点说法filter相当于加油站,request是条路,response是条路,目的地是servlet,这个加油站设在什么地方对什么数据操作可以由你来控制。
过滤的实现
调用链
所有过滤器都服从调用的过滤器链,并通过定义明确的接口得到执行。一个执行过滤器的 Java 类必须执行这一 javax.servlet.Filter 接口。这一接口含有三个过滤器必须执行的方法:
doFilter(ServletRequest, ServletResponse, FilterChain):这是一个完成过滤行为的方法。这同样是上游过滤器调用的方法。引入的FilterChain对象提供了后续过滤器所要调用的信息。
init(FilterConfig):这是一个容器所调用的初始化方法。它保证了在第一次doFilter()调用前由容器调用。您能获取在 web.xml 文件中指定的初始化参数。
destroy():容器在破坏过滤器实例前,doFilter()中的所有活动都被该实例终止后,调用该方法。
嵌套调用在 doFilter() 方法执行中发生。除非您建立一个过滤器明确阻止所有后续处理(通过其它过滤器及资源处理器),否则过滤器一定会在 doFilter 方法中作以下的调用:
FilterChain.doFilter(request, response);
一些需要过滤器的情况:
(1)认证Filter
(2)日志和审核Filter
(3)图片转换Filter
(4)数据压缩Filter
(5)密码Filter
(6)令牌Filter
(7)触发资源访问事件的Filter
(8)XSLT Filter
(9)媒体类型链Filter
1.批量设置请求编码
为了避免提交数据的中文乱码问题,需要在每次使用请求之前设置request.setCharacterEncoding("gb2312")编码格式,麻烦。Filter可以批量拦截修改servlet的请求和响应。
我们编写一个EncodingFilter.java,来批量设置请求编码。
public class EncodingFilter implements Filter {
public void init(FilterConfig config) throws ServletException {}
public void destroy() {}
public void doFilter(ServletRequest request,
ServletResponse response,
FilterChain chain)
throws IOException, ServletException {
request.setCharacterEncoding("gb2312");
chain.doFilter(request, response);
}
}
在此EncodingFilter实现了Filter接口,Filter接口中定义的三个方法都要在EncodingFilter中实现,其中doFilter()的代码实现主要的功能:
为请求设置gb2312编码并执行chain.doFilter()继续下面的操作。
转换成对应HttpServletRequest和HttpServletResponse才能进行下面的session操作和页面重定向。
与servlet相似,为了让filter发挥作用还需要在web.xml进行配置。
<filter>
<filter-name>EncodingFilter</filter-name>
<filter-class>sam.EncodingFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>EncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
filter标签部分定义使用的过滤器,filter-mapping标签告诉服务器把哪些请求交给过滤器处理。这里的/*表示所有请求,/表示根路径,*(星号)代表所有请求,加在一起就变成了根路径下的所有请求。
这样,所有的请求都会先被EncodingFilter拦截,并在请求里设置上指定的gb2312编码。
2.用filter控制用户访问权限
出于信息安全和其他一些原因的考虑,项目中的一些页面要求用户满足了一定条件之后才能访问让用户输入帐号和密码,如果输入的信息正确就在session里做一个成功的标记,这里的成功标志就是session中的username有值;
其后在请求保密信息的时候判断session中是否有已经登录成功的标记,存在则可以访问,不存在则禁止访问。
假设我们要保护的页面是admin/index.jsp
编写SecurityFilter.java,控制用户访问权限
public class SecurityFilter implements Filter {
public void doFilter(ServletRequest request,
ServletResponse response,
FilterChain chain)
throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
HttpSession session = req.getSession();
if (session.getAttribute("username") != null) {
chain.doFilter(request, response);
} else {
res.sendRedirect("../failure.jsp");
}
}
web.xml进行如下配置
<filter>
<filter-name>SecurityFilter</filter-name>
<filter-class>sam.SecurityFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>SecurityFilter</filter-name>
<url-pattern>/admin/*</url-pattern>
</filter-mapping>
定义SecurityFilter过滤器,让它过滤匹配/admin/*的所有请求,/admin/路径下的所有请求都会接受SecurityFilter的检查
因为Filter本来设计成为多种协议服务,http协议仅仅是其中一种,将ServletRequest和ServletResponse转换成HttpServletRequest和HttpServletResponse才能进行下面的session操作和页面重定向。
得到了http请求之后,可以获得请求对应的session,判断session中的username变量是否为null,如果不为null,说明用户已经登录,就可以调用doFilter继续请求访问的资源。如果为null,说明用户还没有登录,禁止用户访问,并使用页面重定向跳转到failure.jsp页面显示提示信息。
因为/failure.jsp的位置在/admin/目录的上一级,所以加上两个点才能正确跳转到failure.jsp,两个点(..)代表当前路径的上一级路径。
3.日志和审核Filter
public class LoggingFilter implements Filter {
private FilterConfig filterConfig = null;
public void init(FilterConfig config) throws ServletException {
this.filterConfig = config;
}
//下面是向服务器控制台输出log,这里做的是演示,更多的是使用log4j
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
String address = request.getRemoteAddr();
filterConfig.getServletContext().log("User IP: " + address);
chain.doFilter(request, response);
}
public void destroy() {
}
}
web.xml配置
<filter>
<filter-name>LoggingFilter</filter-name>
<filter-class>samjava.filter.LoggingFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>LoggingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
4.filter所谓的特性
请求映射filter-mapping和servlet-mapping都是将对应的filter或servlet映射到某个url-pattern上,当客户发起某一请求时,服务器先将此请求与web.xml中定义的所有url-pattern进行匹配,然后执行匹配通过的filter和servlet。
你可以使用三种方式定义url-pattern。
直接映射一个请求。
<servlet-mapping>
<servlet-name>TestServlet</servlet-name>
<url-pattern>/TestServlet</url-pattern>
</servlet-mapping>
映射一个路径下的所有请求。
<servlet-mapping>
<servlet-name>EncodingFilter</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
需要注意的是,这种写法必须以/开头,写成与绝对路径的形式,即便是映射所有请求也要写成/*,不能简化成*。
映射结尾相同的一类请求。
<servlet-mapping>
<servlet-name>ControllerServlet</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
需要注意的是,这种请求映射就不能指定某一路径了,它必须是以星号(*)开始字母结尾,不能写成/*.do的形式。
5.过滤链
我们使用了两个过滤器,EncodingFilter负责设置编码,SecurityFilter负责控制权限,那这两个过滤器是怎么起作用的呢?
所有的奥秘就在Filter中的FilterChain中。服务器会按照web.xml中过滤器定义的先后循序组装成一条链,然后一次执行其中的doFilter()方法。执行的顺序就如上图所示,执行第一个过滤器的chain.doFilter()之前的代码,第二个过滤器的chain.doFilter()之前的代码,请求的资源,第二个过滤器的chain.doFilter()之后的代码,第一个过滤器的chain.doFilter()之后的代码,最后返回响应。
代码执行顺序是:
(1)执行EncodingFilter.doFilter()中chain.doFilter()之前的部分:request.setCharacterEncoding("gb2312");
(2)执行SecurityFilter.doFilter()中chain.doFilter()之前的部分:判断用户是否已登录
(3)如果用户已登录,则访问请求的资源:/admin/index.jsp
(4)如果用户未登录,则页面重定向到:/failure.jsp
(5)执行SecurityFilter.doFilter()中chain.doFilter()之后的部分;
(6)执行EncodingFilter.doFilter()中chain.doFilter()之后的部分;
说的简单点就是filter将按照在web.xml文件中的声明顺序调用。
过滤链的好处是,执行过程中任何时候都可以打断,只要不执行chain.doFilter()就不会再执行后面的过滤器和请求的内容。
要特别注意过滤链的执行顺序问题,像EncodingFilter就一定要放在所有Filter之前(在web.xml文件中),这样才能确保在使用请求中的数据前设置正确的编码。
6.filter的详细配置
我们已经了解了filter的基本用法,还有一些细节配置在特殊情况下起作用。
在servlet-2.3中,Filter会过滤一切请求,包括服务器内部使用forward转发请求和<%@ include file="/index.jsp"%>的情况。
到了servlet-2.4中Filter默认下只拦截外部提交的请求,forward和include这些内部转发都不会被过滤,但是有时候我们需要forward的时候也用到Filter,这样就需要如下配置。
<filter>
<filter-name>TestFilter</filtername>
<filter-class>sam.TestFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>TestFilter</filtername>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
<dispatcher>INCLUDE</dispatcher>
<dispatcher>EXCEPTION</dispatcher>
</filter-mapping>
这样TestFilter就会过滤所有状态下的请求。如果我们没有进行设置,默认使用的就是REQUEST。而EXCEPTION是在isErrorPage="true"的情况下出现的,这个用处不多,看一下即可。
这里FORWARD是解决request.getDispatcher("index.jsp").forward(request, response);无法触发Filter的关键,配置上这个以后再进行forward的时候就可以触发过滤器了。
在使用管道和过滤器模式时,一般会使用以下的GOF设计模式。
1、职责链模式(Chain Of Responsibility)
职责链设计模式的意图就是使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。其实管道和过滤器模式就是职责链模式的抽象,把它应用到软件体系架构中。
2、 命令模式(Command)
命令模式的意图将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤消的操作。在管道和过滤器模式中每个过滤器一般使用命令模式,把请求封装成一个命令进行处理。
3、装饰模式(Decorator)
装饰模式的意图就是动态地给一个对象添加一些额外的职责。就增加功能来说,Decorator模式相比生成子类更为灵活。
在管道和过滤器模式中,在每个过滤器中经常需要对请求进行动态的增加功能,或者修改请求的内容,这时一般会使用装饰模式.如Servlet filter的javax.servlet.http.HttpServletRequestWrapper, javax.servlet.http.HttpServletResponseWrapper就是装饰模式的应用.
一个filter 包括:
1. 在servlet被调用之前截获;
2. 在servlet被调用之前检查servlet request;
3. 根据需要修改request头和request数据;
4. 根据需要修改response头和response数据;
5. 在servlet被调用之后截获.
通俗点说法filter相当于加油站,request是条路,response是条路,目的地是servlet,这个加油站设在什么地方对什么数据操作可以由你来控制。
过滤的实现
调用链
所有过滤器都服从调用的过滤器链,并通过定义明确的接口得到执行。一个执行过滤器的 Java 类必须执行这一 javax.servlet.Filter 接口。这一接口含有三个过滤器必须执行的方法:
doFilter(ServletRequest, ServletResponse, FilterChain):这是一个完成过滤行为的方法。这同样是上游过滤器调用的方法。引入的FilterChain对象提供了后续过滤器所要调用的信息。
init(FilterConfig):这是一个容器所调用的初始化方法。它保证了在第一次doFilter()调用前由容器调用。您能获取在 web.xml 文件中指定的初始化参数。
destroy():容器在破坏过滤器实例前,doFilter()中的所有活动都被该实例终止后,调用该方法。
嵌套调用在 doFilter() 方法执行中发生。除非您建立一个过滤器明确阻止所有后续处理(通过其它过滤器及资源处理器),否则过滤器一定会在 doFilter 方法中作以下的调用:
FilterChain.doFilter(request, response);
一些需要过滤器的情况:
(1)认证Filter
(2)日志和审核Filter
(3)图片转换Filter
(4)数据压缩Filter
(5)密码Filter
(6)令牌Filter
(7)触发资源访问事件的Filter
(8)XSLT Filter
(9)媒体类型链Filter
1.批量设置请求编码
为了避免提交数据的中文乱码问题,需要在每次使用请求之前设置request.setCharacterEncoding("gb2312")编码格式,麻烦。Filter可以批量拦截修改servlet的请求和响应。
我们编写一个EncodingFilter.java,来批量设置请求编码。
public class EncodingFilter implements Filter {
public void init(FilterConfig config) throws ServletException {}
public void destroy() {}
public void doFilter(ServletRequest request,
ServletResponse response,
FilterChain chain)
throws IOException, ServletException {
request.setCharacterEncoding("gb2312");
chain.doFilter(request, response);
}
}
在此EncodingFilter实现了Filter接口,Filter接口中定义的三个方法都要在EncodingFilter中实现,其中doFilter()的代码实现主要的功能:
为请求设置gb2312编码并执行chain.doFilter()继续下面的操作。
转换成对应HttpServletRequest和HttpServletResponse才能进行下面的session操作和页面重定向。
与servlet相似,为了让filter发挥作用还需要在web.xml进行配置。
<filter>
<filter-name>EncodingFilter</filter-name>
<filter-class>sam.EncodingFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>EncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
filter标签部分定义使用的过滤器,filter-mapping标签告诉服务器把哪些请求交给过滤器处理。这里的/*表示所有请求,/表示根路径,*(星号)代表所有请求,加在一起就变成了根路径下的所有请求。
这样,所有的请求都会先被EncodingFilter拦截,并在请求里设置上指定的gb2312编码。
2.用filter控制用户访问权限
出于信息安全和其他一些原因的考虑,项目中的一些页面要求用户满足了一定条件之后才能访问让用户输入帐号和密码,如果输入的信息正确就在session里做一个成功的标记,这里的成功标志就是session中的username有值;
其后在请求保密信息的时候判断session中是否有已经登录成功的标记,存在则可以访问,不存在则禁止访问。
假设我们要保护的页面是admin/index.jsp
编写SecurityFilter.java,控制用户访问权限
public class SecurityFilter implements Filter {
public void doFilter(ServletRequest request,
ServletResponse response,
FilterChain chain)
throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
HttpSession session = req.getSession();
if (session.getAttribute("username") != null) {
chain.doFilter(request, response);
} else {
res.sendRedirect("../failure.jsp");
}
}
web.xml进行如下配置
<filter>
<filter-name>SecurityFilter</filter-name>
<filter-class>sam.SecurityFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>SecurityFilter</filter-name>
<url-pattern>/admin/*</url-pattern>
</filter-mapping>
定义SecurityFilter过滤器,让它过滤匹配/admin/*的所有请求,/admin/路径下的所有请求都会接受SecurityFilter的检查
因为Filter本来设计成为多种协议服务,http协议仅仅是其中一种,将ServletRequest和ServletResponse转换成HttpServletRequest和HttpServletResponse才能进行下面的session操作和页面重定向。
得到了http请求之后,可以获得请求对应的session,判断session中的username变量是否为null,如果不为null,说明用户已经登录,就可以调用doFilter继续请求访问的资源。如果为null,说明用户还没有登录,禁止用户访问,并使用页面重定向跳转到failure.jsp页面显示提示信息。
因为/failure.jsp的位置在/admin/目录的上一级,所以加上两个点才能正确跳转到failure.jsp,两个点(..)代表当前路径的上一级路径。
3.日志和审核Filter
public class LoggingFilter implements Filter {
private FilterConfig filterConfig = null;
public void init(FilterConfig config) throws ServletException {
this.filterConfig = config;
}
//下面是向服务器控制台输出log,这里做的是演示,更多的是使用log4j
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
String address = request.getRemoteAddr();
filterConfig.getServletContext().log("User IP: " + address);
chain.doFilter(request, response);
}
public void destroy() {
}
}
web.xml配置
<filter>
<filter-name>LoggingFilter</filter-name>
<filter-class>samjava.filter.LoggingFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>LoggingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
4.filter所谓的特性
请求映射filter-mapping和servlet-mapping都是将对应的filter或servlet映射到某个url-pattern上,当客户发起某一请求时,服务器先将此请求与web.xml中定义的所有url-pattern进行匹配,然后执行匹配通过的filter和servlet。
你可以使用三种方式定义url-pattern。
直接映射一个请求。
<servlet-mapping>
<servlet-name>TestServlet</servlet-name>
<url-pattern>/TestServlet</url-pattern>
</servlet-mapping>
映射一个路径下的所有请求。
<servlet-mapping>
<servlet-name>EncodingFilter</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
需要注意的是,这种写法必须以/开头,写成与绝对路径的形式,即便是映射所有请求也要写成/*,不能简化成*。
映射结尾相同的一类请求。
<servlet-mapping>
<servlet-name>ControllerServlet</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
需要注意的是,这种请求映射就不能指定某一路径了,它必须是以星号(*)开始字母结尾,不能写成/*.do的形式。
5.过滤链
我们使用了两个过滤器,EncodingFilter负责设置编码,SecurityFilter负责控制权限,那这两个过滤器是怎么起作用的呢?
所有的奥秘就在Filter中的FilterChain中。服务器会按照web.xml中过滤器定义的先后循序组装成一条链,然后一次执行其中的doFilter()方法。执行的顺序就如上图所示,执行第一个过滤器的chain.doFilter()之前的代码,第二个过滤器的chain.doFilter()之前的代码,请求的资源,第二个过滤器的chain.doFilter()之后的代码,第一个过滤器的chain.doFilter()之后的代码,最后返回响应。
代码执行顺序是:
(1)执行EncodingFilter.doFilter()中chain.doFilter()之前的部分:request.setCharacterEncoding("gb2312");
(2)执行SecurityFilter.doFilter()中chain.doFilter()之前的部分:判断用户是否已登录
(3)如果用户已登录,则访问请求的资源:/admin/index.jsp
(4)如果用户未登录,则页面重定向到:/failure.jsp
(5)执行SecurityFilter.doFilter()中chain.doFilter()之后的部分;
(6)执行EncodingFilter.doFilter()中chain.doFilter()之后的部分;
说的简单点就是filter将按照在web.xml文件中的声明顺序调用。
过滤链的好处是,执行过程中任何时候都可以打断,只要不执行chain.doFilter()就不会再执行后面的过滤器和请求的内容。
要特别注意过滤链的执行顺序问题,像EncodingFilter就一定要放在所有Filter之前(在web.xml文件中),这样才能确保在使用请求中的数据前设置正确的编码。
6.filter的详细配置
我们已经了解了filter的基本用法,还有一些细节配置在特殊情况下起作用。
在servlet-2.3中,Filter会过滤一切请求,包括服务器内部使用forward转发请求和<%@ include file="/index.jsp"%>的情况。
到了servlet-2.4中Filter默认下只拦截外部提交的请求,forward和include这些内部转发都不会被过滤,但是有时候我们需要forward的时候也用到Filter,这样就需要如下配置。
<filter>
<filter-name>TestFilter</filtername>
<filter-class>sam.TestFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>TestFilter</filtername>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
<dispatcher>INCLUDE</dispatcher>
<dispatcher>EXCEPTION</dispatcher>
</filter-mapping>
这样TestFilter就会过滤所有状态下的请求。如果我们没有进行设置,默认使用的就是REQUEST。而EXCEPTION是在isErrorPage="true"的情况下出现的,这个用处不多,看一下即可。
这里FORWARD是解决request.getDispatcher("index.jsp").forward(request, response);无法触发Filter的关键,配置上这个以后再进行forward的时候就可以触发过滤器了。
在使用管道和过滤器模式时,一般会使用以下的GOF设计模式。
1、职责链模式(Chain Of Responsibility)
职责链设计模式的意图就是使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。其实管道和过滤器模式就是职责链模式的抽象,把它应用到软件体系架构中。
2、 命令模式(Command)
命令模式的意图将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤消的操作。在管道和过滤器模式中每个过滤器一般使用命令模式,把请求封装成一个命令进行处理。
3、装饰模式(Decorator)
装饰模式的意图就是动态地给一个对象添加一些额外的职责。就增加功能来说,Decorator模式相比生成子类更为灵活。
在管道和过滤器模式中,在每个过滤器中经常需要对请求进行动态的增加功能,或者修改请求的内容,这时一般会使用装饰模式.如Servlet filter的javax.servlet.http.HttpServletRequestWrapper, javax.servlet.http.HttpServletResponseWrapper就是装饰模式的应用.