Java Filter 过滤器详细介绍及实例代码
这里有新鲜出炉的 Java 设计模式, 程序狗速度看过来!
Java 程序设计语言
java 是一种可以撰写跨平台应用软件的面向对象的程序设计语言, 是由 Sun Microsystems 公司于 1995 年 5 月推出的 Java 程序设计语言和 Java 平台 (即 JavaEE(j2ee), JavaME(j2me), JavaSE(j2se)) 的总称
Filter 也称之为过滤器, 它是 Servlet 技术中最实用的技术, 本文章 web 开发人员通过 Filter 技术, 对 web 服务器管理的所有 web 资源进行拦截, 从而实现一些特殊的功能, 本文章将向大家介绍 Java 中的 Filter 过滤器, 需要的朋友可以参考一下
Filter 简介
Filter 也称之为过滤器, 它是 Servlet 技术中最实用的技术, WEB 开发人员通过 Filter 技术, 对 web 服务器管理的所有 web 资源: 例如 Jsp, Servlet, 静态图片文件或静态 html 文件等进行拦截, 从而实现一些特殊的功能例如实现 URL 级别的权限访问控制过滤敏感词汇压缩响应信息等一些高级功能
它主要用于对用户请求进行预处理, 也可以对 HttpServletResponse 进行后处理使用 Filter 的完整流程: Filter 对用户请求进行预处理, 接着将请求交给 Servlet 进行处理并生成响应, 最后 Filter 再对服务器响应进行后处理
Filter 功能
1. 在 HttpServletRequest 到达 Servlet 之前, 拦截客户的 HttpServletRequest 根据需要检查 HttpServletRequest , 也可以修改 HttpServletRequest 头和数据
2. 在 HttpServletResponse 到达客户端之前, 拦截 HttpServletResponse 根据需要检查 HttpServletResponse , 也可以修改 HttpServletResponse 头和数据
如何借助 Filter 实现拦截功能
Filter 接口中有一个 doFilter 方法, 当开发人员编写好 Filter, 并配置对哪个 web 资源进行拦截后, WEB 服务器每次在调用 web 资源的 service 方法之前, 都会先调用一下 filter 的 doFilter 方法, 因此, 在该方法内编写代码可达到如下目的:
1. 调用目标资源之前, 让一段代码执行
2. 是否调用目标资源(即是否让用户访问 web 资源)
web 服务器在调用 doFilter 方法时, 会传递一个 filterChain 对象进来, filterChain 对象是 filter 接口中最重要的一个对象, 它也提供了一个 doFilter 方法, 开发人员可以根据需求决定是否调用此方法, 调用该方法, 则 web 服务器就会调用 web 资源的 service 方法, 即 web 资源就会被访问, 否则 web 资源不会被访问
Filter 开发两步走
编写 java 类实现 Filter 接口, 并实现其 doFilter 方法
在 web.xml 文件中使用和元素对编写的 filter 类进行注册, 并设置它所能拦截的资源
web.xml 配置各节点介绍:
<filter > 指定一个过滤器
<filter-name > 用于为过滤器指定一个名字, 该元素的内容不能为空
<filter-class > 元素用于指定过滤器的完整的限定类名
<init-param > 元素用于为过滤器指定初始化参数, 它的子元素 < param-name > 指定参数的名字,<param-value > 指定参数的值
在过滤器中, 可以使用 FilterConfig 接口对象来访问初始化参数
<filter-mapping > 元素用于设置一个 Filter 所负责拦截的资源一个 Filter 拦截的资源可通过两种方式来指定: Servlet 名称和资源访问的请求路径
<filter-name > 子元素用于设置 filter 的注册名称该值必须是在 < filter > 元素中声明过的过滤器的名字
<url-pattern > 设置 filter 所拦截的请求路径(过滤器关联的 URL 样式)
<servlet-name > 指定过滤器所拦截的 Servlet 名称
<dispatcher > 指定过滤器所拦截的资源被 Servlet 容器调用的方式, 可以是 REQUEST,INCLUDE,FORWARD 和 ERROR 之一, 默认 REQUEST 用户可以设置多个 < dispatcher > 子元素用来指定 Filter 对资源的多种调用方式进行拦截
<dispatcher > 子元素可以设置的值及其意义
REQUEST: 当用户直接访问页面时, Web 容器将会调用过滤器如果目标资源是通过 RequestDispatcher 的 include()或 forward()方法访问时, 那么该过滤器就不会被调用
INCLUDE: 如果目标资源是通过 RequestDispatcher 的 include()方法访问时, 那么该过滤器将被调用除此之外, 该过滤器不会被调用
FORWARD: 如果目标资源是通过 RequestDispatcher 的 forward()方法访问时, 那么该过滤器将被调用, 除此之外, 该过滤器不会被调用
ERROR: 如果目标资源是通过声明式异常处理机制调用时, 那么该过滤器将被调用除此之外, 过滤器不会被调用
Filter 链
在一个 web 应用中, 可以开发编写多个 Filter, 这些 Filter 组合起来称之为一个 Filter 链
web 服务器根据 Filter 在 web.xml 文件中的注册顺序, 决定先调用哪个 Filter, 当第一个 Filter 的 doFilter 方法被调用时, web 服务器会创建一个代表 Filter 链的 FilterChain 对象传递给该方法在 doFilter 方法中, 开发人员如果调用了 FilterChain 对象的 doFilter 方法, 则 web 服务器会检查 FilterChain 对象中是否还有 filter, 如果有, 则调用第 2 个 filter, 如果没有, 则调用目标资源
Filter 的生命周期
public void init(FilterConfig filterConfig) throws ServletException;// 初始化
和我们编写的 Servlet 程序一样, Filter 的创建和销毁由 WEB 服务器负责 web 应用程序启动时, web 服务器将创建 Filter 的实例对象, 并调用其 init 方法, 读取 web.xml 配置, 完成对象的初始化功能, 从而为后续的用户请求作好拦截的准备工作 (filter 对象只会创建一次, init 方法也只会执行一次) 开发人员通过 init 方法的参数, 可获得代表当前 filter 配置信息的 FilterConfig 对象
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException;// 拦截请求
这个方法完成实际的过滤操作当客户请求访问与过滤器关联的 URL 的时候, Servlet 过滤器将先执行 doFilter 方法 FilterChain 参数用于访问后续过滤器
public void destroy();// 销毁
Filter 对象创建后会驻留在内存, 当 web 应用移除或服务器停止时才销毁在 Web 容器卸载 Filter 对象之前被调用该方法在 Filter 的生命周期中仅执行一次在这个方法中, 可以释放过滤器使用的资源
FilterConfig 接口
用户在配置 filter 时, 可以使用为 filter 配置一些初始化参数, 当 web 容器实例化 Filter 对象, 调用其 init 方法时, 会把封装了 filter 初始化参数的 filterConfig 对象传递进来因此开发人员在编写 filter 时, 通过 filterConfig 对象的方法, 就可获得以下内容:
- String getFilterName();// 得到 filter 的名称
- String getInitParameter(String name);// 返回在部署描述中指定名称的初始化参数的值如果不存在返回 null.
- Enumeration getInitParameterNames();// 返回过滤器的所有初始化参数的名字的枚举集合
- public ServletContext getServletContext();// 返回 Servlet 上下文对象的引用
Filter 使用案例
使用 Filter 验证用户登录安全控制
前段时间参与维护一个项目, 用户退出系统后, 再去地址栏访问历史, 根据 url, 仍然能够进入系统响应页面我去检查一下发现对请求未进行过滤验证用户登录添加一个 filter 搞定问题!
先在 web.xml 配置
- <filter>
- <filter-name>
- SessionFilter
- </filter-name>
- <filter-class>
- com.action.login.SessionFilter
- </filter-class>
- <init-param>
- <param-name>
- logonStrings
- </param-name>
- <!-- 对登录页面不进行过滤 -->
- <param-value>
- /project/index.jsp;login.do
- </param-value>
- </init-param>
- <init-param>
- <param-name>
- includeStrings
- </param-name>
- <!-- 只对指定过滤参数后缀进行过滤 -->
- <param-value>
- .do;.jsp
- </param-value>
- </init-param>
- <init-param>
- <param-name>
- redirectPath
- </param-name>
- <!-- 未通过跳转到登录界面 -->
- <param-value>
- /index.jsp
- </param-value>
- </init-param>
- <init-param>
- <param-name>
- disabletestfilter
- </param-name>
- <!-- Y: 过滤无效 -->
- <param-value>
- N
- </param-value>
- <!-- http://www.manongjc.com/article/1613.html -->
- </init-param>
- </filter>
- <filter-mapping>
- <filter-name>
- SessionFilter
- </filter-name>
- <url-pattern>
- /*
- </url-pattern>
- </filter-mapping>
- 接着编写 FilterServlet.java: package com.action.login; 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; import javax.servlet.http.HttpServletResponseWrapper;
- /** * 判断用户是否登录, 未登录则退出系统 * http://www.manongjc.com/article/1613.html */
- public class SessionFilter implements Filter { public FilterConfig config;
- public void destroy() { this.config = null; } public static boolean isContains(String
- container, String[] regx) { boolean result = false; for (int i = 0; i
- < regx.length; i++) { if (container.indexOf(regx[i]) !=- 1) { return true;
- } } return result; } public void doFilter(ServletRequest request, ServletResponse
- response, FilterChain chain) throws IOException, ServletException { HttpServletRequest
- hrequest=( HttpServletRequest) request; HttpServletResponseWrapper wrapper=n
- ew HttpServletResponseWrapper((HttpServletResponse) response); String logonStrings=c
- onfig.getInitParameter( "logonStrings"); // 登录登陆页面 String includeStrings=c
- onfig.getInitParameter( "includeStrings"); // 过滤资源后缀参数 String redirectPath=h
- request.getContextPath() + config.getInitParameter( "redirectPath"); //
- 没有登陆转向页面 String disabletestfilter=c onfig.getInitParameter(
- "disabletestfilter"); // 过滤器是否有效 if (disabletestfilter.toUpperCase().equals( "Y")) { // 过滤无效
- chain.doFilter(request, response); return; } String[] logonList=l ogonStrings.split(
- ";"); String[] includeList=i ncludeStrings.split( ";"); if (!this.isContains(hrequest.getRequestURI(),
- includeList)) { // 只对指定过滤参数后缀进行过滤 chain.doFilter(request, response); return;
- } if (this.isContains(hrequest.getRequestURI(), logonList)) { // 对登录页面不进行过滤
- chain.doFilter(request, response); return; } String user=( String) hrequest.getSession().getAttribute(
- "useronly"); // 判断用户是否登录 if (user==n ull) { wrapper.sendRedirect(redirectPath);
- return; } else { chain.doFilter(request, response); return; } } public
- void init(FilterConfig filterConfig) throws ServletException { config=f
- ilterConfig; } }
这样既可完成对用户所有请求, 均要经过这个 Filter 进行验证用户登录
防止中文乱码过滤器
项目使用 spring 框架时当前台 JSP 页面和 JAVA 代码中使用了不同的字符集进行编码的时候就会出现表单提交的数据或者上传 / 下载中文名称文件出现乱码的问题, 那就可以使用这个过滤器
- <filter>
- <filter-name>encoding</filter-name>
- <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
- <init-param>
- <param-name>encoding</param-name><!-- 用来指定一个具体的字符集 -->
- <param-value>UTF-8</param-value>
- </init-param>
- <init-param>
- <param-name>forceEncoding</param-name><!--true: 无论 request 是否指定了字符集, 都是用 encoding;false: 如果 request 已指定一个字符集, 则不使用 encoding-->
- <param-value>false</param-value>
- </init-param>
- </filter>
- <filter-mapping>
- <filter-name>encoding</filter-name>
- <url-pattern>/*</url-pattern>
- </filter-mapping>
Spring+Hibernate 的 OpenSessionInViewFilter 控制 session 的开关
当 hibernate+spring 配合使用的时候, 如果设置了 lazy=true(延迟加载), 那么在读取数据的时候, 当读取了父数据后, hibernate 会自动关闭 session, 这样, 当要使用与之关联数据子数据的时候, 系统会抛出 lazyinit 的错误, 这时就需要使用 spring 提供的 OpenSessionInViewFilter 过滤器
OpenSessionInViewFilter 主要是保持 Session 状态直到 request 将全部页面发送到客户端, 直到请求结束后才关闭 session, 这样就可以解决延迟加载带来的问题
注意: OpenSessionInViewFilter 配置要写在 struts2 的配置前面因为 tomcat 容器在加载过滤器的时候是按照顺序加载的, 如果配置文件先写的是 struts2 的过滤器配置, 然后才是 OpenSessionInViewFilter 过滤器配置, 所以加载的顺序导致, action 在获得数据的时候 session 并没有被 spring 管理
- <filter><!-- lazy loading enabled in spring -->
- <filter-name>OpenSessionInViewFilter</filter-name>
- <filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class>
- <init-param>
- <param-name>sessionFactoryBeanName</param-name><!-- 可缺省默认是从 spring 容器中找 id 为 sessionFactory 的 bean, 如果 id 不为 sessionFactory, 则需要配置如下, 此处 SessionFactory 为 spring 容器中的 bean -->
- <param-value>sessionFactory</param-value>
- </init-param>
- <init-param>
- <param-name>singleSession</param-name><!-- singleSession 默认为 true, 若设为 false 则等于没用 OpenSessionInView -->
- <param-value>true</param-value>
- </init-param>
- </filter>
- <filter-mapping>
- <filter-name>OpenSessionInViewFilter</filter-name>
- <url-pattern>*.do</url-pattern>
- </filter-mapping>
Struts2 的 web.xml 配置
项目中使用 Struts2 同样需要在 web.xml 配置过滤器, 用来截取请求, 转到 Struts2 的 Action 进行处理
注意: 如果在 2.1.3 以前的 Struts2 版本, 过滤器使用 org.apache.struts2.dispatcher.FilterDispatcher 否则使用 org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter 从 Struts2.1.3 开始, 将废弃 ActionContextCleanUp 过滤器, 而在 StrutsPrepareAndExecuteFilter 过滤器中包含相应的功能
三个初始化参数配置:
config 参数: 指定要加载的配置文件逗号分割
actionPackages 参数: 指定 Action 类所在的包空间逗号分割
configProviders 参数: 自定义配置文件提供者, 需要实现 ConfigurationProvider 接口类逗号分割
- <!-- struts 2.x filter -->
- <filter>
- <filter-name>struts2</filter-name>
- <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
- </filter>
- <filter-mapping>
- <filter-name>struts2</filter-name>
- <url-pattern>*.do</url-pattern>
- </filter-mapping>
来源: http://www.phperz.com/article/18/0205/359238.html