1,Filter的生命周期
和我们编写的Servlet程序一样,Filter的创建和销毁由WEB服务器负责。 web 应用程序启动时,web 服务器将创建Filter的实例对象,并调用其init方法,完成对象的初始化功能,从而为后续的用户请求作好拦截的准备工作(注:filter对象只会创建一次,init方法也只会执行一次。)。在Web容器卸载 Filter 对象之前调用destroy方法。该方法在Filter的生命周期中仅执行一次。在这个方法中,可以释放过滤器使用的资源。
2,FilterConfig(类比ServletConfig)
用户在配置filter时,可以使用<init-param>为filter配置一些初始化参数,当web容器实例化Filter对象,调用其init方法时,会把封装了filter初始化参数的filterConfig对象传递进来。因此开发人员在编写filter时,通过filterConfig对象的方法,就可获得:
•StringgetFilterName():得到filter的名称。
•StringgetInitParameter(String name): 返回在部署描述中指定名称的初始化参数的值。如果不存在返回null.
•EnumerationgetInitParameterNames():返回过滤器的所有初始化参数的名字的枚举集合。
•publicServletContext getServletContext():返回Servlet上下文对象的引用。
3,Filter部署注意点
<filter-mapping>元素用于设置一个Filter 所负责拦截的资源。一个Filter拦截的资源可通过两种方式来指定:Servlet 名称和资源访问的请求路径
•<filter-name>子元素用于设置filter的注册名称。该值必须是在<filter>元素中声明过的过滤器的名字
•<url-pattern>设写法和Servlet一样
•<dispatcher>指定过滤器所拦截的资源被 Servlet 容器调用的方式,可以是REQUEST,INCLUDE,FORWARD和ERROR之一,默认REQUEST。用户可以设置多个<dispatcher> 子元素用来指定 Filter 对资源的多种调用方式进行拦截。
<dispatcher>子元素可以设置的值及其意义:
•REQUEST:当用户直接访问页面时,Web容器将会调用过滤器。如果目标资源是通过RequestDispatcher的include()或forward()方法访问时,那么该过滤器就不会被调用。
•INCLUDE:如果目标资源是通过RequestDispatcher的include()方法访问时,那么该过滤器将被调用。除此之外,该过滤器不会被调用。
•FORWARD:如果目标资源是通过RequestDispatcher的forward()方法访问时,那么该过滤器将被调用,除此之外,该过滤器不会被调用。
•ERROR:如果目标资源是通过声明式异常处理机制调用时,那么该过滤器将被调用。除此之外,过滤器不会被调用。
4,Filter常见应用
[1]统一全站字符编码的过滤器(但是只能处理post,需要增强后处理get)
[2]禁止浏览器缓存所有动态页面的过滤器:
•有 3 个 HTTP 响应头字段都可以禁止浏览器缓存当前页面,它们在 Servlet 中的示例代码如下:
•response.setDateHeader("Expires",-1);
•response.setHeader("Cache-Control","no-cache");
•response.setHeader("Pragma","no-cache");
•并不是所有的浏览器都能完全支持上面的三个响应头,因此最好是同时使用上面的三个响应头。
•Expires数据头:值为GMT时间值,为-1指浏览器不要缓存页面
•Cache-Control响应头有两个常用值:
•no-cache指浏览器不要缓存当前页面。
•max-age:xxx指浏览器缓存页面xxx秒。
[3]控制浏览器缓存页面中的静态资源的过滤器:
场景:有些动态页面中引用了一些图片或css文件以修饰页面效果,这些图片和css文件经常是不变化的,所以为减轻服务器的压力,可以使用filter控制浏览器缓存这些文件,以提升服务器的性能
[4]使用Filter实现URL级别的权限认证
•情景:在实际开发中我们经常把一些执行敏感操作的servlet映射到一些特殊目录中,并用filter把这些特殊目录保护起来,限制只能拥有相应访问权限的用户才能访问这些目录下的资源。从而在我们系统中实现一种URL级别的权限功能。
[5]实现用户自动登陆的过滤器
•在用户登陆成功后,发送一个名称为user的cookie给客户端,cookie的值为用户名和md5加密后的密码。
•编写一个AutoLoginFilter,这个filter检查用户是否带有名称为user的cookie来,如果有,则调用dao查询cookie的用户名和密码是否和数据库匹配,匹配则向session中存入user对象(即用户登陆标记),以实现程序完成自动登陆。
5,Filter高级开发
由于开发人员在filter中可以得到代表用户请求和响应的request、response对象,因此在编程中可以使用Decorator(装饰器)模式对request、response对象进行包装,再把包装对象传给目标资源,从而实现一些特殊需求。
[request对象的增强]
Servlet API 中提供了一个request对象的Decorator设计模式的默认实现类HttpServletRequestWrapper ,(HttpServletRequestWrapper 类实现了request 接口中的所有方法,但这些方法的内部实现都是仅仅调用了一下所包装的的request 对象的对应方法)以避免用户在对request对象进行增强时需要实现request接口中的所有方法。
案例:
l使用Decorator模式包装request对象,完全解决get、post请求方式下的乱码问题。
l使用Decorator模式包装request对象,实现html标签转义功能(Tomcat服务器中提供了转义html标签的工具类)。
[response对象的增强]
lServlet API 中提供了response对象的Decorator设计模式的默认实现类HttpServletResponseWrapper ,(HttpServletResponseWrapper类实现了response接口中的所有方法,但这些方法的内部实现都是仅仅调用了一下所包装的的response对象的对应方法)以避免用户在对response对象进行增强时需要实现response接口中的所有方法。
案列:
l应用HttpServletResponseWrapper对象,压缩响应正文内容。
思路:通过filter向目标页面传递一个自定义的response对象。在自定义的response对象中,重写getOutputStream方法和getWriter方法,使目标资源调用此方法输出页面内容时,获得的是我们自定义的ServletOutputStream对象。在我们自定义的ServletOuputStream对象中,重写write方法,使写出的数据写出到一个buffer中。当页面完成输出后,在filter中就可得到页面写出的数据,从而我们可以调用GzipOuputStream对数据进行压缩后再写出给浏览器,以此完成响应正文件压缩功能。
6,代码片段
6.1--[真正解决全站乱码--request增强]
//真正解决全站乱码
public class CharacterEncodingFilter2 implements Filter {
public void doFilter(ServletRequest req, ServletResponse resp,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
request.setCharacterEncoding("UTF-8"); //post get
response.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=UTF-8");
chain.doFilter(new MyRequest(request), response); //request.getparameter("password");
}
/*
1.写一个类,实现与被增强对象相同的接口
2.定义一个变量,记住被增强对象
3.定义一个构造方法,接收被增强对象
4.覆盖想增强的方法
5.对于不想增强的方法,直接调用被增强对象(目标对象)的方法
*/
class MyRequest extends HttpServletRequestWrapper{
private HttpServletRequest request;
public MyRequest(HttpServletRequest request) {
super(request);
this.request = request;
}
@Override
public String getParameter(String name) {
String value = this.request.getParameter(name);
if(!request.getMethod().equalsIgnoreCase("get")){
return value;
}
if(value==null){
return null;
}
try {
return value = new String(value.getBytes("iso8859-1"),request.getCharacterEncoding());
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
}
......
}
6.2--[禁止浏览器缓存所有动态页面的过滤器]
public class NoCacheFilter implements Filter {
public void doFilter(ServletRequest req, ServletResponse resp,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
response.setDateHeader("Expires",-1);
response.setHeader("Cache-Control","no-cache");
response.setHeader("Pragma","no-cache");
chain.doFilter(request, response);
}
//控制浏览器缓存的过滤器
public class CacheFilter implements Filter {
private FilterConfig config;
public void doFilter(ServletRequest req, ServletResponse resp,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
//1.获取到用户想访问的资源
String uri = request.getRequestURI();
//2.获取该资源的缓存时间
int expires = 0;
if(uri.endsWith(".jpg")){
expires = Integer.parseInt(this.config.getInitParameter("jpg"));
}else if(uri.endsWith(".css")){
expires = Integer.parseInt(this.config.getInitParameter("css"));
}else{
expires = Integer.parseInt(this.config.getInitParameter("js"));
}
response.setDateHeader("expires", System.currentTimeMillis()+expires*60*1000);
chain.doFilter(request, response);
}
import sun.misc.BASE64Encoder;
public class AutoLoginFilter implements Filter {
public void doFilter(ServletRequest req, ServletResponse resp,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
// 1.先检查用户是否已登陆,没登陆才自动登陆
User user = (User) request.getSession().getAttribute("user");
if (user != null) {
chain.doFilter(request, response);
return;
}
// 2.没登陆,再执行自动登陆逻辑
// 看用户有没有带自动登陆的cookie
Cookie autoLoginCookie = null;
Cookie cookies[] = request.getCookies();
for (int i = 0; cookies != null && i < cookies.length; i++) {
if (cookies[i].getName().equals("autologin")) {
autoLoginCookie = cookies[i];
}
}
if (autoLoginCookie == null) {
chain.doFilter(request, response);
return;
}
// 用户带了自动登陆的cookie,则先检查cookie的有效期
String values[] = autoLoginCookie.getValue().split("\\:");
if (values.length != 3) {
chain.doFilter(request, response);
return;
}
long expirestime = Long.parseLong(values[1]);
if (System.currentTimeMillis() > expirestime) {
chain.doFilter(request, response);
return;
}
// 代表cookie时间有效,再检查cookie的有效性
String username = values[0];
String client_md5 = values[2];
BusinessService service = new BusinessService();
user = service.findUser(username);
if (user == null) {
chain.doFilter(request, response);
return;
}
// //autologin=username:expirestime:md5(password:expirestime:username)
String server_md5 = md5(user.getUsername(), user.getPassword(),
expirestime);
if (!server_md5.equals(client_md5)) {
chain.doFilter(request, response);
return;
}
// 执行登陆
request.getSession().setAttribute("user", user);
chain.doFilter(request, response);
}
private String md5(String username, String password, long expirestime) {
try {
String value = password + ":" + expirestime + ":" + username;
MessageDigest md = MessageDigest.getInstance("md5");
byte md5[] = md.digest(value.getBytes());
BASE64Encoder encode = new BASE64Encoder();
return encode.encode(md5);
} catch (Exception e) {
throw new RuntimeException(e);
}
}......
}
6.5--[压缩响应]
public class GzipFilter implements Filter {
public void doFilter(ServletRequest req, ServletResponse resp,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
MyResponse myresponse = new MyResponse(response);
chain.doFilter(request, myresponse); //response.getwriter response.getOutputStream
//取出缓冲的数据压缩后输出
byte out[] = myresponse.getBuffer(); //得到目标资源的输出
System.out.println("压之前:" + out.length);
byte gzipout[] = gzip(out);
System.out.println("压之后:" + gzipout.length);
response.setHeader("content-encoding", "gzip");
response.setHeader("content-length", gzipout.length + "");
response.getOutputStream().write(gzipout);
}
public byte[] gzip(byte b[]) throws IOException{
ByteArrayOutputStream bout = new ByteArrayOutputStream();
GZIPOutputStream gout = new GZIPOutputStream(bout);
gout.write(b);
gout.close();
return bout.toByteArray();
}
class MyResponse extends HttpServletResponseWrapper{
private ByteArrayOutputStream bout = new ByteArrayOutputStream();
private PrintWriter pw;
private HttpServletResponse response;
public MyResponse(HttpServletResponse response) {
super(response);
this.response = response;
}
@Override
public ServletOutputStream getOutputStream() throws IOException {
return new MyServletOutputStream(bout); //myresponse.getOutputStream().write("hahah");
}
@Override
public PrintWriter getWriter() throws IOException {
pw = new PrintWriter(new OutputStreamWriter(bout,response.getCharacterEncoding()));
return pw; //MyResponse.getWriter().write("中国");
}
public byte[] getBuffer(){
if(pw!=null){
pw.close();
}
return bout.toByteArray();
}
}
class MyServletOutputStream extends ServletOutputStream{
private ByteArrayOutputStream bout;
public MyServletOutputStream(ByteArrayOutputStream bout){
this.bout = bout;
}
@Override
public void write(int b) throws IOException {
bout.write(b);
}
}
}
6.6--[字符转义过滤]
public class HtmlFilter implements Filter {
public void doFilter(ServletRequest req, ServletResponse resp,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
chain.doFilter(new MyRequest(request), response); //request.getParameter("resume"); //<script>
}
class MyRequest extends HttpServletRequestWrapper{
private HttpServletRequest request;
public MyRequest(HttpServletRequest request) {
super(request);
this.request = request;
}
@Override
public String getParameter(String name) {
String value = this.request.getParameter(name);
if(value==null){
return null;
}
return filter(value);
}
public String filter(String message) {
if (message == null)
return (null);
char content[] = new char[message.length()];
message.getChars(0, message.length(), content, 0);
StringBuffer result = new StringBuffer(content.length + 50);
for (int i = 0; i < content.length; i++) {
switch (content[i]) {
case '<':
result.append("<");
break;
case '>':
result.append(">");
break;
case '&':
result.append("&");
break;
case '"':
result.append(""");
break;
default:
result.append(content[i]);
}
}
return (result.toString());
}
}