zoukankan      html  css  js  c++  java
  • servlet过滤器

    一、简介

    (一)概述

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

    (二) Filter 的实现

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

    1)实现 javax.servlet.Filter 接口,实现其 init、doFilter、destroy 三个方法。

    2)实现在web.xml中的配置。

    2、javax.servlet.Filter 接口

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

    ①、init:只会在 web 程序加载的时候调用,即启动如tomcat等服务器时调用。一般负责加载配置的参数。

    ②、destroy :web程序卸载的时候调用。一般负责关闭某些容器等。

    ③、doFilter:每次客户端请求都会调用一次。Filter 的所有工作基本都集中在该方法中进行。 

    package servlet.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
     * @author SAM-SHO 
     * @Date 2014-9-25
     */
    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 才能生效。

    <!-- 过滤器配置 -->
        <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>

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

    二、常用 Filter 

    (一)字符编码的 Filter 

    1、字符编码的 Filter 几乎每个项目都会用到。代码如下:

    package servlet.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 编码过滤器
     * @description
     * @author SAM-SHO
     * @Date 2014-10-12
     */
    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/ 下面的所有资源有效。代码如下:

    package servlet.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 javax.servlet.http.HttpServletResponse;
    
    /**
     * 
     * RefererFilter.java
     * 
     * @title 责任链过滤器
     * @description
     * @author SAM-SHO
     * @Date 2014-12-9
     */
    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、配置如下:

      

        <!--责任链过滤器  -->
        <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、为了方便,权限配置在文件中:

    package servlet.filter;
    
    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、代码如下:

    package servlet.filter.gzip;
    
    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);
        }
    }
        <!-- 压缩过滤器 -->
        <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(", ", "
    "));
    
        }
    
    }

      

        <!--  文件上传 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>
  • 相关阅读:
    IIS使用十大原则,(IIS过期时间,IIS缓存设置) 【转载】
    微软中国的相关研发团队 交流平台
    Excel文件减肥和加速
    二进制与字符串之间的转换
    调度模式·WorkerChannelRequest
    引用Hibernate的Java桌面程序打包方法
    [收藏]Open CASCADE
    需要天天读月月读年年读的书
    geotools的dotnet版本
    FC4 下ns2.28的安装 (zz)
  • 原文地址:https://www.cnblogs.com/sunli0205/p/5901697.html
Copyright © 2011-2022 走看看