过滤器Filter详解

一、简介
(一)概述
       Filter,过滤器,用于在servlet之外对request 和response 进行修改。Filter 有一个 FilterChain 的概念,一个FilterChain 包括多个 Filter。客户端请求 request在抵达servlet 之前,会经过 FilterChain 里面所有的 Filter,服务器响应 response 从servlet抵达客户端浏览器之前也会经过 FilterChain 里面所有的 Filter 。

(二) Filter 的实现

1、实现自定义的 Filter 需要满足一下条件:

  • 实现 javax.servlet.Filter 接口,实现其 init、doFilter、destroy 三个方法。
  • 实现在web.xml中的配置。

2、javax.servlet.Filter 接口

   Filter 接口有三个方法:这三个方法反应了  Filter 的生命周期。

  • init:只会在 web 程序加载的时候调用,即启动如tomcat等服务器时调用。一般负责加载配置的参数。
  • destroy :web程序卸载的时候调用。一般负责关闭某些容器等。
  • doFilter:每次客户端请求都会调用一次。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;  
import javax.servlet.http.HttpServletRequest;  
 
import org.apache.log4j.Logger;  
/**
 *  
 * MyFilter.java
 *
 * @title 过滤器
 * @description
 */  
public class MyFilter implements Filter {  
 
    private Logger logger = Logger.getLogger(this.getClass());  
      
    public void destroy() {  
 
    }  
 
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException {  
          
        HttpServletRequest request = (HttpServletRequest) req;  
          
        String contextPath = request.getContextPath();//上下文路径  
        String servletPath = request.getServletPath();//得到访问的servlet或者jsp的路径  
          
        logger.debug("上下文路径:"+contextPath);  
        logger.debug("访问的servlet或者jsp的路径 : "+servletPath);  
                  
        chain.doFilter(req, resp);  
 
    }  
 
    public void init(FilterConfig filterConfig) throws ServletException {  
 
        String name =  filterConfig.getInitParameter("name");  
        logger.debug("获取过滤器的初始化参数: " + name);  
    }  
 

3、 配置 Filter:每个过滤器需要配置在web.xml中才能生效,一个Filter需要配置<filter> 和 <filter-mapping>标签。

1)<filter>  :配置 Filter 名称,实现类以及初始化参数。可以同时配置多个初始化参数。
2)<filter-mapping> :配置什么规则下使用这个Filter 。
①<url-pattern> :配置url的规则,可以配置多个,也可以使用通配符(*)。例如 /jsp/* 适用于本ContextPath下以“/jsp/ ”开头    的所有servlet路径, *.do 适用于所有以“ .do”结尾的servlet路径。

②<dispatcher> :配置到达servlet的方式,可以同时配置多个。有四种取值:REQUEST、FORWARD、ERROR、INCLUDE。如果没有配置,则默认为REQUEST。它们的区别是:

  • REQUEST :表示仅当直接请求servlet时才生效。
  • FORWARD :表示仅当某servlet通过forward转发到该servlet时才生效。
  • INCLUDE :Jsp中可以通过<jsp:include/>请求某servlet, 只有这种情况才有效。
  • ERROR :Jsp中可以通过<%@page errorPage="error.jsp" %>指定错误处理页面,仅在这种情况下才生效。

③<url-pattern>和<dispatcher> 是且的关系,只有满足<url-pattern>的条件,且满足<dispatcher>的条件,该Filter 才能生效。

扫描二维码关注公众号,回复: 9240229 查看本文章
<!-- 过滤器配置 -->  
    <filter>  
        <filter-name>MyFilter</filter-name>  
        <filter-class>servlet.filter.MyFilter</filter-class>  
        <init-param>  
            <param-name>name</param-name>  
            <param-value>Sam-Sho</param-value>  
        </init-param>  
    </filter>  
    <filter-mapping>  
        <filter-name>MyFilter</filter-name>  
        <url-pattern>/jsp/*</url-pattern>  
        <url-pattern>*.do</url-pattern>  
 
        <dispatcher>REQUEST</dispatcher>  
        <dispatcher>FORWARD</dispatcher>  
    </filter-mapping> 

3)总结:一个Web程序可以配置多个Filter ,访问有先后顺序,<filter-mapping> 配置在前面的Filter 执行要早于配置在后面的Filter 。

二、常用 Filter

(一)字符编码的 Filter

1、字符编码的 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;  
 
/**
 *  
 * CharacterEncodingFilter.java
 *  
 * @title 编码过滤器
 */  
public class CharacterEncodingFilter implements Filter {  
 
    private String characterEncoding;  
    private boolean enabled;//是否启用  
 
    public void init(FilterConfig config) throws ServletException {  
 
        // 获取配置好的参数,  
        characterEncoding = config.getInitParameter("characterEncoding");//配置好的字符编码  
        enabled = "true".equalsIgnoreCase(config.getInitParameter("enabled"));//是否启用  
    }  
 
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {  
 
        //设置字符编码  
        if (enabled && characterEncoding != null) {  
            request.setCharacterEncoding(characterEncoding);  
            response.setCharacterEncoding(characterEncoding);  
        }  
 
        chain.doFilter(request, response);//调用下一个过滤器  
    }  
 
    public void destroy() {  
        characterEncoding = null;//注销的时候,设为空  
    }  

2、web.xml 配置如下:

<!-- 编码过滤器 -->  
<filter>  
    <filter-name>CharacterEncodingFilter</filter-name>  
    <filter-class>servlet.filter.CharacterEncodingFilter</filter-class>  
    <init-param>  
        <param-name>characterEncoding</param-name>  
        <param-value>UTF-8</param-value>  
    </init-param>  
    <init-param>  
        <param-name>enabled</param-name>  
        <param-value>true</param-value>  
    </init-param>  
</filter>  
<filter-mapping>  
    <filter-name>CharacterEncodingFilter</filter-name>  
    <url-pattern>/*</url-pattern>  
</filter-mapping> 

(二)防盗链 Filter

1、防盗链需要使用到请求头 Referer ,该 Filter  的配置仅对 /images/ 和 /upload/images/ 下面的所有资源有效。代码如下:

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;  
import javax.servlet.http.HttpServletRequest;  
import javax.servlet.http.HttpServletResponse;  
 
/**
 *  
 * RefererFilter.java
 *  
 * @title 责任链过滤器
 */  
public class RefererFilter implements Filter {  
 
    public void init(FilterConfig config) throws ServletException {  
    }  
 
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {  
 
        // 必须的  
        HttpServletRequest request = (HttpServletRequest) req;  
        HttpServletResponse response = (HttpServletResponse) res;  
 
        // 禁止缓存  
        response.setHeader("Cache-Control", "no-store");  
        response.setHeader("Pragrma", "no-cache");  
        response.setDateHeader("Expires", 0);  
 
        // 链接来源地址,通过获取请求头 referer 得到  
        String referer = request.getHeader("referer");  
        System.out.println("获取的来源--->: " + referer);  
 
        if (referer == null || !referer.contains(request.getServerName())) {//本站点访问,则有效  
 
            /**
             * 如果 链接地址来自其他网站,则返回错误图片
             */  
            request.getRequestDispatcher("/error.gif").forward(request, response);  
 
        } else {  
 
            /**
             * 图片正常显示
             */  
            chain.doFilter(request, response);  
        }  
 
    }  
 
    public void destroy() {  
    }  

2、web.xml 配置如下:

<!--责任链过滤器  -->  
<filter>  
    <filter-name>RefererFilter</filter-name>  
    <filter-class>servlet.filter.RefererFilter</filter-class>  
</filter>  
<filter-mapping>  
    <filter-name>RefererFilter</filter-name>  
    <url-pattern>/images/*</url-pattern>  
    <url-pattern>/upload/images/*</url-pattern>  
</filter-mapping>

(三)权限校验 Filter

1、为了方便,权限配置在文件中:

import java.io.FileInputStream;  
import java.io.IOException;  
import java.util.Properties;  
 
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;  
 
 
public class PrivilegeFilter implements Filter {  
 
    private Properties pp = new Properties();//读取配置文件  
 
    public void init(FilterConfig config) throws ServletException {  
 
        // 从 初始化参数 中获取权 限配置文件 的位置  
        String file = config.getInitParameter("file");  
        String realPath = config.getServletContext().getRealPath(file);  
        try {  
            pp.load(new FileInputStream(realPath));  
        } catch (Exception e) {  
            config.getServletContext().log("读取权限控制文件失败。", e);  
        }  
    }  
 
    public void doFilter(ServletRequest req, ServletResponse res,  
            FilterChain chain) throws IOException, ServletException {  
 
        HttpServletRequest request = (HttpServletRequest) req;  
 
        // 获取访问的路径,例如:admin.jsp  
        String requestURI = request.getRequestURI().replace(  
                request.getContextPath() + "/", "");  
 
        // 获取 action 参数,例如:add  
        String action = req.getParameter("action");  
        action = action == null ? "" : action;  
 
        // 拼接成 URI。例如:log.do?action=list  
        String uri = requestURI + "?action=" + action;  
 
        // 从 session 中获取用户权限角色。  
        String role = (String) request.getSession(true).getAttribute("role");  
        role = role == null ? "guest" : role;  
 
        boolean authentificated = false;  
        // 开始检查该用户角色是否有权限访问 uri  
        for (Object obj : pp.keySet()) {  
            String key = ((String) obj);  
            // 使用正则表达式验证 需要将 ? . 替换一下,并将通配符 * 处理一下  
            if (uri.matches(key.replace("?", "\\?").replace(".", "\\.")  
                    .replace("*", ".*"))) {  
                // 如果 role 匹配  
                if (role.equals(pp.get(key))) {  
                    authentificated = true;  
                    break;  
                }  
            }  
        }  
        if (!authentificated) {  
            System.out.println("您无权访问该页面。请以合适的身份登陆后查看。");  
        }  
        // 继续运行  
        chain.doFilter(req, res);  
    }  
 
    public void destroy() {  
        pp = null;  
    }  


2、web.xml配置如下:

<!-- 权限过滤器 -->  
<filter>  
    <filter-name>privilegeFilter</filter-name>  
    <filter-class>  
        servlet.filter.PrivilegeFilter  
    </filter-class>  
    <init-param>  
        <param-name>file</param-name>  
        <param-value>/WEB-INF/classes/privilege.properties</param-value>  
    </init-param>  
</filter>  
<filter-mapping>  
    <filter-name>privilegeFilter</filter-name>  
    <url-pattern>*.do</url-pattern>  
</filter-mapping>

3、权限配置如下:

# Privilege Settings  
 
admin.do?action\=*      =   administrator  
log.do?action\=*        =   administrator  
 
list.do?action\=add     =   member  
list.do?action\=delete  =   member  
list.do?action\=save    =   member  
 
list.do?action\=view    =   guest  
list.do?action\=list    =   guest 

(四)GZIP 压缩 Filter

1、使用servlet 的对响应内容进行压缩:

private void GZipTest(HttpServletResponse response) throws IOException {  
    //实现压缩  
    String tDate = "准备被压缩的数据";  
    System.out.println("压缩前的数据大小:  "+tDate.getBytes().length);  
      
    ByteArrayOutputStream bout = new ByteArrayOutputStream();  
    GZIPOutputStream gout = new GZIPOutputStream(bout);  
    gout.write(tDate.getBytes());  
    gout.flush();  
    gout.finish();  
    gout.close();//写到字节数组流中  
      
    byte[] gzip = bout.toByteArray();//得到压缩后的数据  
    System.out.println("压缩后的数据大小:  "+gzip.length);  
      
    // 通知浏览器数据采用压缩格式  
    response.setHeader("Content-Encoding", "gzip");//压缩格式  
    response.setHeader("Content-Length",gzip.length+"" );//压缩数据的长度  
    response.getOutputStream().write(gzip);  

2、使用过滤器代码:

1)GZIP 压缩的核心是 JDK 自带的压缩数据的类,GZIPOutputStream 。
2)响应头:Content-Encoding 和 Content-Length 。
3)GZipResponseWrapper 类为自定义的 Response 类,内部对输出的内容进行 GZIP 的压缩。

3、代码如下:

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;  
import javax.servlet.http.HttpServletRequest;  
import javax.servlet.http.HttpServletResponse;  
 
/**
 *  
 * GZipFilter.java
 *  
 * @title 压缩过滤器
 * @description
 * @author SAM-SHO
 * @Date 2014-12-9
 */  
public class GZipFilter implements Filter {  
 
    public void destroy() {  
    }  
 
    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {  
 
        HttpServletRequest request = (HttpServletRequest) req;  
        HttpServletResponse response = (HttpServletResponse) res;  
 
        //获取浏览器支持的压缩格式  
        String acceptEncoding = request.getHeader("Accept-Encoding");  
        System.out.println("Accept-Encoding: " + acceptEncoding);  
 
        if (acceptEncoding != null && acceptEncoding.toLowerCase().indexOf("gzip") != -1) {  
 
            // 如果客户浏览器支持 GZIP 格式, 则使用 GZIP 压缩数据  
            GZipResponseWrapper gzipResponse = new GZipResponseWrapper(response);  
            chain.doFilter(request, gzipResponse);  
 
            // 输出压缩数据  
            gzipResponse.getOutputStream();  
            gzipResponse.finishResponse();  
 
        } else {  
            // 否则, 不压缩  
            chain.doFilter(request, response);  
        }  
    }  
 
    public void init(FilterConfig arg0) throws ServletException {  
    }  

package servlet.filter.gzip;  
 
import java.io.IOException;  
import java.io.OutputStreamWriter;  
import java.io.PrintWriter;  
 
import javax.servlet.ServletOutputStream;  
import javax.servlet.http.HttpServletResponse;  
import javax.servlet.http.HttpServletResponseWrapper;  
 
/**
 *  
 * GZipResponseWrapper.java
 *
 * @title 封装的Response ,不会真正输出到客户端
 * 继承 HttpServletResponseWrapper,其实现了 HttpServletResponse 接口
 * @description
 * @author SAM-SHO  
 * @Date 2014-12-9
 */  
public class GZipResponseWrapper extends HttpServletResponseWrapper {  
 
    // 默认的 response  
    private HttpServletResponse response;  
 
    // 自定义的 outputStream, 执行close()的时候对数据压缩,并输出  
    private GZipOutputStream gzipOutputStream;  
 
    // 自定义 printWriter,将内容输出到 GZipOutputStream 中  
    private PrintWriter writer;  
 
    public GZipResponseWrapper(HttpServletResponse response) throws IOException {  
        super(response);  
        this.response = response;  
    }  
    @Override  
    public ServletOutputStream getOutputStream() throws IOException {  
        if (gzipOutputStream == null)  
            gzipOutputStream = new GZipOutputStream(response);  
        return gzipOutputStream;  
    }  
    @Override  
    public PrintWriter getWriter() throws IOException {  
        if (writer == null)  
            writer = new PrintWriter(new OutputStreamWriter(  
                    new GZipOutputStream(response), "UTF-8"));  
        return writer;  
    }  
 
    // 压缩后数据长度会发生变化 因此将该方法内容置空  
    @Override  
    public void setContentLength(int contentLength) {  
    }  
    @Override  
    public void flushBuffer() throws IOException {  
        gzipOutputStream.flush();  
    }  
 
    public void finishResponse() throws IOException {  
        if (gzipOutputStream != null)  
            gzipOutputStream.close();  
        if (writer != null)  
            writer.close();  
    }  
}
package servlet.filter.gzip;  
 
import java.io.ByteArrayOutputStream;  
import java.io.IOException;  
import java.util.zip.GZIPOutputStream;  
 
import javax.servlet.ServletOutputStream;  
import javax.servlet.http.HttpServletResponse;  
 
/**
 *  
 * GZipOutputStream.java
 *
 * @title 自定义的压缩流,内部调用JDK自带的压缩流
 * @description
 * @author SAM-SHO  
 * @Date 2014-12-9
 */  
public class GZipOutputStream extends ServletOutputStream {  
 
    private HttpServletResponse response;  
 
    // JDK 自带的压缩数据的类  
    private GZIPOutputStream gzipOutputStream;  
 
    // 将压缩后的数据存放到 ByteArrayOutputStream 对象中  
    private ByteArrayOutputStream byteArrayOutputStream;  
 
    public GZipOutputStream(HttpServletResponse response) throws IOException {  
        this.response = response;  
        byteArrayOutputStream = new ByteArrayOutputStream();  
        gzipOutputStream = new GZIPOutputStream(byteArrayOutputStream);  
    }  
    @Override  
    public void write(int b) throws IOException {  
        gzipOutputStream.write(b);  
    }  
    @Override  
    public void close() throws IOException {  
 
        // 压缩完毕 一定要调用该方法  
        gzipOutputStream.finish();  
 
        // 将压缩后的数据输出到客户端  
        byte[] content = byteArrayOutputStream.toByteArray();  
 
        // 设定压缩方式为 GZIP, 客户端浏览器会自动将数据解压  
        response.addHeader("Content-Encoding", "gzip");  
        response.addHeader("Content-Length", Integer.toString(content.length));  
 
        // 输出  
        ServletOutputStream out = response.getOutputStream();  
        out.write(content);  
        out.close();  
    }  
 
    @Override  
    public void flush() throws IOException {  
        gzipOutputStream.flush();  
    }  
    @Override  
    public void write(byte[] b, int off, int len) throws IOException {  
        gzipOutputStream.write(b, off, len);  
    }  
    @Override  
    public void write(byte[] b) throws IOException {  
        gzipOutputStream.write(b);  
    }  

4、web.xml配置如下:

<!-- 压缩过滤器 -->  
<filter>  
    <filter-name>gzipFilter</filter-name>  
    <filter-class>servlet.filter.gzip.GZipFilter</filter-class>  
</filter>  
<filter-mapping>  
    <filter-name>gzipFilter</filter-name>  
    <url-pattern>/*</url-pattern>  
</filter-mapping>

(四)文件上传 Filter

1、上传文件,修改<form> 标签的 enctype 设置为 “multipart/form-data” 。这样就可以通过获取请求头 Content-type 判断是否为文件上传。
2、使用 commons-fileupload-1.2.1.jar 实现上传。

package servlet.filter.upload;  
 
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;  
import javax.servlet.http.HttpServletRequest;  
 
/**
 *  
 * UploadFilter.java
 *
 * @title 文件上传 Filter  
 * @description
 * @author SAM-SHO  
 * @Date 2014-12-9
 */  
public class UploadFilter implements Filter {  
 
    public void destroy() {  
 
    }  
 
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)

throws IOException, ServletException {  
 
        UploadRequestWrapper uploadRequest = new UploadRequestWrapper((HttpServletRequest) request);  
 
        chain.doFilter(uploadRequest, response);  
 
    }  
 
    public void init(FilterConfig filterConfig) throws ServletException {  
 
    }  
 
}

package servlet.filter.upload;  
 
import java.io.File;  
import java.io.FileOutputStream;  
import java.io.OutputStream;  
import java.util.HashMap;  
import java.util.Iterator;  
import java.util.List;  
import java.util.Map;  
 
import javax.servlet.http.HttpServletRequest;  
import javax.servlet.http.HttpServletRequestWrapper;  
 
import org.apache.commons.fileupload.DiskFileUpload;  
import org.apache.commons.fileupload.FileItem;  
 
/**
 *  
 * UploadRequestWrapper.java
 *  
 * @title 文件上传自定义Request
 * @description
 * @author SAM-SHO
 * @Date 2014-12-9
 */  
public class UploadRequestWrapper extends HttpServletRequestWrapper {  
 
    private static final String MULTIPART_HEADER = "Content-type";  
 
    // 是否是上传文件  
    private boolean multipart;  
 
    // map,保存所有的域  
    private Map<String, Object> params = new HashMap<String, Object>();  
 
    @SuppressWarnings("all")  
    public UploadRequestWrapper(HttpServletRequest request) {  
 
        super(request);  
 
        // 判断是否为上传文件  
        multipart = request.getHeader(MULTIPART_HEADER) != null   
                    && request.getHeader(MULTIPART_HEADER).startsWith("multipart/form-data");  
 
        //是文件上传  
        if (multipart) {  
 
            try {  
                // 使用apache的工具解析  
                DiskFileUpload upload = new DiskFileUpload();//代替 DiskFileUpload   
                upload.setHeaderEncoding("utf8");  
 
                // 解析,获得所有的文本域与文件域  
                List<FileItem> fileItems = upload.parseRequest(request);  
 
                for (Iterator<FileItem> it = fileItems.iterator(); it.hasNext();) {  
 
                    // 遍历  
                    FileItem item = it.next();  
                    if (item.isFormField()) {  
 
                        // 如果是文本域,直接放到map里  
                        params.put(item.getFieldName(), item.getString("utf8"));  
 
                    } else {  
 
                        // 否则,为文件,先获取文件名称  
                        String filename = item.getName().replace("\\", "/");  
                        filename = filename.substring(filename.lastIndexOf("/") + 1);  
 
                        // 保存到系统临时文件夹中  
                        File file = new File(System.getProperty("java.io.tmpdir"), filename);  
 
                        // 保存文件内容  
                        OutputStream ous = new FileOutputStream(file);  
                        ous.write(item.get());  
                        ous.close();  
 
                        // 放到map中  
                        params.put(item.getFieldName(), file);  
                    }  
                }  
 
            } catch (Exception e) {  
                e.printStackTrace();  
            }  
        }  
    }

@Override  
    public Object getAttribute(String name) {  
 
        // 如果为上传文件,则从map中取值  
        if (multipart && params.containsKey(name)) {  
            return params.get(name);  
        }  
        return super.getAttribute(name);  
    }  
 
    @Override  
    public String getParameter(String name) {  
 
        // 如果为上传文件,则从map中取值  
        if (multipart && params.containsKey(name)) {  
            return params.get(name).toString();  
        }  
        return super.getParameter(name);  
    }  
 
    public static void main(String[] args) {  
 
        System.out.println(System.getProperties().toString().replace(", ", "\r\n"));  
 
    }  
 

3、web.xml配置如下: 

<!--  文件上传 Filter -->  
<filter>  
    <filter-name>uploadFilter</filter-name>  
    <filter-class>servlet.filter.upload.UploadFilter</filter-class>  
</filter>  
<filter-mapping>  
    <filter-name>uploadFilter</filter-name>  
    <url-pattern>/*</url-pattern>  
</filter-mapping> 

原文链接:https://yq.aliyun.com/ziliao/311692

参考链接:https://www.cnblogs.com/ygj0930/p/6374212.html

发布了33 篇原创文章 · 获赞 3 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/weixin_37189727/article/details/85319112