1.Servlet过滤器
1.1 什么是过滤器
过滤器是一个程序,它先于与之相关的servlet或JSP页面运行在服务器上。过滤器可附加到一个或多个servlet或JSP页面上,并且可以检查进入这些资源的请求信息。在这之后,过滤器可以作如下的选择:
①以常规的方式调用资源(即,调用servlet或JSP页面)。
②利用修改过的请求信息调用资源。
③调用资源,但在发送响应到客户机前对其进行修改。
④阻止该资源调用,代之以转到其他的资源,返回一个特定的状态代码或生成替换输出。
1.2 Servlet过滤器的基本原理
在Servlet作为过滤器使用时,它可以对客户的请求进行处理。处理完成后,它会交给下一个过滤器处理,这样,客户的请求在过滤链里逐个处理,直到请求发送到目标为止。例如,某网站里有提交“修改的注册信息”的网页,当用户填写完修改信息并提交后,服务器在进行处理时需要做两项工作:判断客户端的会话是否有效;对提交的数据进行统一编码。这两项工作可以在由两个过滤器组成的过滤链里进行处理。当过滤器处理成功后,把提交的数据发送到最终目标;如果过滤器处理不成功,将把视图派发到指定的错误页面。
2.Servlet过滤器开发步骤
开发Servlet过滤器的步骤如下:
①编写实现Filter接口的Servlet类。
②在web.xml中配置Filter。
开发一个过滤器需要实现Filter接口,Filter接口定义了以下方法:
①destory()由Web容器调用,初始化此Filter。
②init(FilterConfig filterConfig)由Web容器调用,初始化此Filter。
③doFilter(ServletRequest request,ServletResponse response,FilterChain chain)具体过滤处理代码。
3.一个过滤器框架实例
SimpleFilter1.java
package com.zj.sample;
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 SimpleFilter1 implements Filter {
@SuppressWarnings("unused")
private FilterConfig filterConfig;
public void init(FilterConfig config) throws ServletException {
this.filterConfig = config;
}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) {
try {
System.out.println("Within SimpleFilter1:Filtering the Request...");
chain.doFilter(request, response);// 把处理发送到下一个过滤器
System.out .println("Within SimpleFilter1:Filtering the Response...");
} catch (IOException ioe) {
ioe.printStackTrace();
} catch (ServletException se) {
se.printStackTrace();
}
}
public void destroy() {
this.filterConfig = null;
}
}
SimpleFilter2.java
package com.zj.sample;
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 SimpleFilter2 implements Filter {
@SuppressWarnings("unused")
private FilterConfig filterConfig;
public void init(FilterConfig config) throws ServletException {
this.filterConfig = config;
}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) {
try {
System.out.println("Within SimpleFilter2:Filtering the Request...");
chain.doFilter(request, response); // 把处理发送到下一个过滤器
System.out.println("Within SimpleFilter2:Filtering the Response...");
} catch (IOException ioe) {
ioe.printStackTrace();
} catch (ServletException se) {
se.printStackTrace();
}
}
public void destroy() {
this.filterConfig = null;
}
}
web.xml
filter1 com.zj.sample.SimpleFilter1 filter1 public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException { System.out.println("Within BannedAccessFilter:Filtering the Request..."); HttpServletRequest req = (HttpServletRequest) request; String requestingHost = req.getRemoteHost(); String referringHost = getReferringHost(req.getHeader("Referer")); String bannedSite = null; boolean isBanned = false; if (bannedSiteTable.contains(requestingHost)) { bannedSite = requestingHost; isBanned = true; } else if (bannedSiteTable.contains(referringHost)) { bannedSite = referringHost; isBanned = true; } if (isBanned) { showWarning(response, bannedSite); } else { chain.doFilter(request, response); } System.out.println("Within BannedAccessFilter:Filtering the Response..."); } public void init(FilterConfig config) throws ServletException { bannedSiteTable = new HashSet (); String bannedSites = config.getInitParameter("bannedSites"); // Default token set: white space. StringTokenizer tok = new StringTokenizer(bannedSites); while (tok.hasMoreTokens()) { String bannedSite = tok.nextToken(); bannedSiteTable.add(bannedSite); System.out.println("Banned " + bannedSite); } } public void destroy() {} private String getReferringHost(String refererringURLString) { try { URL referringURL = new URL(refererringURLString); return (referringURL.getHost()); } catch (MalformedURLException mue) { // Malformed or null return (null); } } // Replacement response that is returned to users // who are from or referred here by a banned site. private void showWarning(ServletResponse response, String bannedSite) throws ServletException, IOException { response.setContentType("text/html"); PrintWriter out = response.getWriter(); String docType = "n"; out.println(docType + "n" + " Access Prohibited n" + "n" + "Access Prohibitedn" + "Sorry, access from or via " + bannedSite + "n" + "is not allowed.n" + ""); } }
web.xml
BannedAccessFilter com.zj.sample.BannedAccessFilter bannedSites [url]www.competingsite.com[/url] [url]www.bettersite.com[/url] [url]www.moreservlets.com[/url] 127.0.0.1//我们测试这个 BannedAccessFilter public class CharArrayWrapper extends HttpServletResponseWrapper { private CharArrayWriter charWriter; public CharArrayWrapper(HttpServletResponse response) { super(response); charWriter = new CharArrayWriter(); } public PrintWriter getWriter() { return (new PrintWriter(charWriter)); } public String toString() { return (charWriter.toString()); } public char[] toCharArray() { return (charWriter.toCharArray()); } }
7.3 替换过滤器
这里展示前一节中给出的CharArrayWrapper的一个常见的应用:更改一个多次出现的目标串为某个替代串的过滤器。
7.3.1通用替换过滤器
ReplaceFilter.java给出一个过滤器,它在CharArraryWrapper中包装响应,传递该包装器到FilterChain对象的doFilter方法中,提取一个给出所有资源的输出的String型值,用一个替代串替换某个目标串的所有出现,并发送此修改过的结果到客户机。
关于这个过滤器,有两件事情需要注意。首先,它是一个抽象类。要使用它,必须建立一个提供getTargetString和getReplacementString方法的实现的子类。下一小节中给出了这种处理的一个例子。其次,它利用一个较小的实用类(见FilterUtils.java)来进行实际的串替换。你可使用新的常规表达式包而不是使用String和StringTokenizer中低级的和繁琐的方法。
ReplaceFilter.java
package com.zj.sample;
import java.io.IOException;
import java.io.PrintWriter;
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.HttpServletResponse;
public abstract class ReplaceFilter implements Filter {
private FilterConfig config;
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws ServletException, IOException {
CharArrayWrapper responseWrapper = new CharArrayWrapper(
(HttpServletResponse) response);
// Invoke resource, accumulating output in the wrapper.
chain.doFilter(request, responseWrapper);
// Turn entire output into one big String.
String responseString = responseWrapper.toString();
// In output, replace all occurrences of target string with replacement
// string.
responseString = FilterUtils.replace(responseString, getTargetString(),
getReplacementString());
// Update the Content-Length header.
updateHeaders(response, responseString);
PrintWriter out = response.getWriter();
out.write(responseString);
}
public void init(FilterConfig config) throws ServletException {
this.config = config;
}
protected FilterConfig getFilterConfig() {
return (config);
}
public void destroy() {
}
public abstract String getTargetString();
public abstract String getReplacementString();
public void updateHeaders(ServletResponse response, String responseString) {
response.setContentLength(responseString.length());
}
}
FilterUtils.java
package com.zj.sample;
public class FilterUtils {
public static String replace(String mainString, String orig,
String replacement) {
String result = "";
int oldIndex = 0;
int index = 0;
int origLength = orig.length();
while ((index = mainString.indexOf(orig, oldIndex)) != -1) {
result = result + mainString.substring(oldIndex, index)
+ replacement;
oldIndex = index + origLength;
}
result = result + mainString.substring(oldIndex);
return (result);
}
}
7.3.2实现一个字符替换过滤器
假设百度收购了google(只是假设),现在所有的页面上凡是出现google字样的文字都必须替换为百度!ReplaceSiteNameFilter.java继承上文ReplaceFilter.java来实现这一功能。
ReplaceSiteNameFilter.java
package com.zj.sample;
public class ReplaceSiteNameFilter extends ReplaceFilter {
public String getTargetString() {
return ("google.com.cn");
}
public String getReplacementString() {
return ("baidu.com");
}
}
web.xml
ReplaceSiteNameFilter com.zj.sample.ReplaceSiteNameFilter ReplaceSiteNameFilter /login.jsp
测试结果:
过滤前
过滤后
8.压缩过滤器
有几个最新的浏览器可处理压缩的内容,自动解开以gzip作为Content-Encoding响应头值的压缩文件,然后就像对原文档那样处理结果。发送这样的压缩内容可以节省很多时间,因为在服务器上压缩文档,然后在客户机上解开文档所需的时间与下载文件的时间相比是微不足道的。程序LongServlet.java给出了一个具有很长的、重复的纯文本输出的servlet,这是一个可供压缩使用的成熟的servlet。如果使用gzip,它可以把输出结果压缩到1/300!
在浏览器支持这个压缩能力时,压缩过滤器可利用章节7介绍的CharArrayWrapper来压缩内容,完成此任务需要下列内容:
1)实现Filter接口的类。这个类名为CompressionFIlter。init方法存放FilterConfig对象在一个字段中,以防子类需要访问servlet环境或过滤器名。destory方法体为空。
2)包装的响应对象。DoFilter方法将ServletResponse对象包装在一个CharArrayWrapper中,并传递此包装器到FilterChain对象的doFilter方法上。在此调用完成后,所有其他过滤器和最终资源都已执行,且输出结果位于包装器之内。这样,原doFilter提取一个代表所有资源的输出的字符数组。如果客户机指出它支持压缩(即,以gzip作为Accept-Encoding头的一个值),则过滤器附加一个GZIPOutputStream到ByteArrayOutputStream上,将字符数组复制到此流中,并设置Content-Encoding响应头为gzip。如果客户机不支持gzip,则将未修改过的字符数组复制到ByteArrayOutputStream。最后,doFilter通过将整个字符数组(可能是压缩过的)写到与original响应相关的OutputStream中,发送结果到客户机。
3)对LongServlet进行注册。
CompressionFilter.java
package com.zj.sample;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.util.zip.GZIPOutputStream;
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;
public class CompressionFilter implements Filter {
private FilterConfig config;
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws ServletException, IOException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
if (!isGzipSupported(req)) {
// Invoke resource normally.
chain.doFilter(req, res);
} else {
// Tell browser we are sending it gzipped data.
res.setHeader("Content-Encoding", "gzip");
// Invoke resource, accumulating output in the wrapper.
CharArrayWrapper responseWrapper = new CharArrayWrapper(res);
chain.doFilter(req, responseWrapper);
// Get character array representing output.
char[] responseChars = responseWrapper.toCharArray();
// Make a writer that compresses data and puts it into a byte array.
ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
GZIPOutputStream zipOut = new GZIPOutputStream(byteStream);
OutputStreamWriter tempOut = new OutputStreamWriter(zipOut);
// Compress original output and put it into byte array.
tempOut.write(responseChars);
// Gzip streams must be explicitly closed.
tempOut.close();
// Update the Content-Length header.
res.setContentLength(byteStream.size());
// Send compressed result to client.
OutputStream realOut = res.getOutputStream();
byteStream.writeTo(realOut);
}
}
public void init(FilterConfig config) throws ServletException {
this.config = config;
}
protected FilterConfig getFilterConfig() {
return (config);
}
public void destroy() {}
private boolean isGzipSupported(HttpServletRequest req) {
String browserEncodings = req.getHeader("Accept-Encoding");
return ((browserEncodings != null) && (browserEncodings.indexOf("gzip") != -1));
}
}
LongServlet.java
package com.zj.sample;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class LongServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
String docType = "n";
String title = "Long Page";
out.println(docType + "n" + "" + title
+ " n" + "n"
+ "" + title + "n");
String line = "Blah, blah, blah, blah, blah. "
+ "Yadda, yadda, yadda, yadda.";
for (int i = 0; i < 10000; i++) {
out.println(line);
}
out.println("");
}
}
web.xml
CompressionFilter com.zj.sample.CompressionFilter CompressionFilter LongServlet LongServlet com.zj.sample.LongServlet LongServlet /LongServlet



