前言
我们知道实际开发中我们可能有这种需要:对于某个网站来说需要限制一些ip对该网站的访
问。例如教育网站,通常我们只能访问到它的首页,至于教务管理的页面,不在该教育网段范
围的IP是不能访问到的。
另外,在实际的项目中我们可能涉及到字符的转码,如果在每个页面都去设置转码会十分的
麻烦,这时就可以使用Filter来解决了。
Filter
Servlet过滤器Fileter是一个小型的web组件,它们通过拦截请求和响应,以便查看、提取或
以某种方式操作客户端和服务器之间交换的数据,实现“过滤”的功能。Filter通常封装了一些功
能的web组件,过滤器提供了一种面向对象的模块化机制,将任务封装到一个可插入的组件中,
Filter组件通过配置文件来声明,并动态的代理。
简单来说Servlet的Filter是:
● 声明式的:通过在web.xml配置文件中声明,允许添加、删除过滤器,而无需改动任何
应用程序代码或jsp页面。
● 灵活的:过滤器可用于客户端的直接调用执行预处理和后期的处理工作,通过过滤链可
以实现一些灵活的功能。
● 可移植的:由于现今各个web容器都是以Servlet的规范进行设计的,因此Servlet过滤器
同样是跨容器的。
● 可重用的:基于其可移植性和声明式的配置方式,Filter是可重用的。
总的来说,Servlet的过滤器是通过一个配置文件来灵活的声明的模块化可重用组件。
过滤器动态的截获传入的请求和传出的响应,在不修改程序代码的情况下,透明的添加或删除
他们。其独立于任何平台和web容器。
Filter体系结构
如其名字所暗示的一样,Servlet过滤器用于拦截传入的请求和传出的响应,并监视、修改
处理web工程中的数据流。过滤器是一个可插入的自由组件。
web资源可以不配置过滤器、也可以配置单个过滤器,也可以配置多个过滤器,形成一个
过滤器链。Filter接受用户的请求,并决定将请求转发给链中的下一个组件,或者终止请求
直接向客户端返回一个响应。如果请求被转发了,它将被传递给链中的下一个过滤器(以
web.xml过滤器的配置顺序为标准)。这个请求在通过过滤链并被服务器处理之后,一个响应
将以相反的顺序通过该链发送回去。这样,请求和响应都得到了处理。
Filter可以应用在客户端和Servlet之间、servlet和serlvet或jsp之间,以及jsp之间。并且
可以通过配置信息,灵活的使用那个过滤器。
Filter工作原理
基于Filter体系结构的描述,我们可以看出Filter的工作原理,简单的通过一幅流程图加以
演示:
客户端浏览器在访问web服务器的某个具体资源的时候,经过过滤器1中code1代码块的相
关处理之后,将request请求传递给过滤链中的下一个过滤器2,(过滤链的顺序以配置文件
中的顺序为基准)过滤器2处理完之后,request就传递的Servlet完成相应的逻辑。
返回响应的过程类似,只是过滤链的顺序相反,这里就不多说了。
Filter的创建过程
在具体去实现FIlter之前,先去看看Filter的源码,简单的看看实现Filter需要做的事,从
源码中可以看出,要编写一个过滤器必须实现Filter接口。实现其接口规定的方法。
★ 实现javax.servlet.Filter接口
★ 实现init方法,读取过滤器的初始化参数
★ 实现doFilter方法,完成对请求或响应的过滤
★ 调用FilterChain接口对象的doFilter方法,向后续的过滤器传递请求或响应
一个简单的字符编码处理的过滤器实现:
package com.kiritor.filter;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
public class EncodeFilter implements Filter{
//定义替换后的字符集,从过滤器的配置参数中读取
String newCharSet;
public void destroy(){
}
public void doFilter(ServletRequest request, ServletResponse response,FilterChain chain) throws IOException, ServletException
{
//处理请求字符集
request.setCharacterEncoding(newCharSet);
//传递给下一个过滤器。这里没有下一个,作为过滤器的规则和良好的编程习惯,应该加上
chain.doFilter(request,response);
//处理响应字符集
response.setContentType("text/html;charset="+newCharSet);
}
public void init(FilterConfig filterConfig)throws ServletException
{
//从过滤器的配置中获得初始化参数,如果没有就使用默认值
if(filterConfig.getInitParameter("newcharset")!=null)
{
newCharSet = filterConfig.getInitParameter("newcharset");
}
else
newCharSet = "GB2312";
}
}
这里对于过滤器对请求和响应的具体"过滤"过程,笔者就不多提了。
Filter的配置
Filter是一个可插入的web组件,必须在web.xml文件中配置才有效,那么Filter是如何
配置的呢?针对上述字符编码的Filter,配置信息如下:
<!-- 指定过滤器的名字,初始化参数等信息 -->
<filter>
<display-name>EncodeFilter</display-name>
<filter-name>EncodeFilter</filter-name>
<filter-class>com.kiritor.filter.EncodeFilter</filter-class>
</filter>
<!-- 指定过滤器的URL关联/*表示所有的 -->
<filter-mapping>
<filter-name>EncodeFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
url-pattern配置详解
在web.xml文件中,以下语法用于定义映射:
l 以”/’开头和以”/*”结尾的是用来做路径映射的。
l 以前缀”*.”开头的是用来做扩展映射的。
l “/” 是用来定义default servlet映射的。
l 剩下的都是用来定义详细映射的。比如: /aa/bb/cc.action
所以,为什么定义”/*.action”这样一个看起来很正常的匹配会错?
因为这个匹配即属于路径映射,也属于扩展映射,导致容器无法判断。
Filter编程实例演示:
接下来笔者将演示一个IP过滤器,过滤掉某个具体的ip,并将客户端的访问记录做
一个日志文件记录。实际的过程如下:
这里笔者保留了字符处理的Filter,打印信息,以便更好的理解过滤链的执行情况。
编写IP过滤器:IPFilter
package com.kiritor.filter;
import java.io.IOException;
import java.net.InetAddress;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.kiritor.logUtil.LogUtil;
/**
* Servlet Filter implementation class IPFilter
*/
public class IPFilter implements Filter {
/**
* Default constructor.
*/
private String ip;
private FilterConfig config;
public IPFilter() {
// TODO Auto-generated constructor stub
}
/**
* @see Filter#destroy()
*/
public void destroy() {
// TODO Auto-generated method stub
}
/**
* @see Filter#doFilter(ServletRequest, ServletResponse, FilterChain)
*/
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest req= (HttpServletRequest)request;
HttpServletResponse res = (HttpServletResponse)response;
// 获取客户请求lP
/*下面那条注释语句得到的是IPV6地址,
* 客户端和服务器在同一台机子上是得不到ipv4地址的**/
//String remoteIP = request.getRemoteAddr();
//以这种方式获取IPv4地址
InetAddress inet = InetAddress.getLocalHost();
LogUtil.logRecord(inet.getHostAddress());
//System.out.println(remoteIP);
if (inet.getHostAddress().equals("192.168.0.3")){
req.getRequestDispatcher("ipErr.jsp").forward(request, response);
} else {
chain.doFilter(request, response);// 调用过滤链上的下一个过滤器
}
System.out.println("IPFilter处理");
chain.doFilter(request, response);
}
/**
* @see Filter#init(FilterConfig)
*/
public void init(FilterConfig fConfig) throws ServletException {
//从过滤器的配置中获得初始化参数,如果没有就使用默认值
this.config = fConfig;
if(fConfig.getInitParameter("ipinfo")!=null)
{
ip = fConfig.getInitParameter("ipinfo");
}
else
ip = "192.168.0.3";
}
}
接下来进行配置:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
<display-name>Servlet_Filter</display-name>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
<filter>
<display-name>EncodeFilter</display-name>
<filter-name>EncodeFilter</filter-name>
<filter-class>com.kiritor.filter.EncodeFilter</filter-class>
<init-param>
<param-name>newcharset</param-name>
<param-value>gb2312</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>EncodeFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter>
<display-name>IPFilter</display-name>
<filter-name>IPFilter</filter-name>
<filter-class>com.kiritor.filter.IPFilter</filter-class>
<init-param>
<param-name>ipinfo</param-name>
<param-value>192.168.0.3</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>IPFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
接下来简单的日志文件操作:
package com.kiritor.logUtil;
import java.io.FileOutputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
/**
* @author Kiritor
* 2013-6-15 上午8:21:19
* 功能:实现IP访问的日志记录
*/
public class LogUtil {
public static void logRecord(String ip)
{
try{
FileOutputStream out = new FileOutputStream("D:\\log.txt",true);
Date date = new Date();
SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd a hh:mm:ss");
String str = f.format(date);
String runningMsg = str+"--->\r\n"+
ip +"登录"
+"\r\n";
out.write(runningMsg.getBytes());
out.close();
}catch(Exception e){
e.printStackTrace();
}
}
}
ok,我们看看实际的运行效果吧!
日志文档信息记录如下:
2013-06-15 上午 08:56:59--->
192.168.0.3登录
2013-06-15 上午 09:00:08--->
192.168.0.3登录
好了,对于Servlet过滤器的学习就到这个地方了,当然上述实例只是Filter的简单运用
十分的灵活与方便。
参考文档:
http://www.ibm.com/developerworks/cn/java/j-pj2ee10/index.html
By Kiritor
2013 /06 /15