1、Filter可以做什么
Filter(过滤器)对web服务器管理的web资源进行拦截,从而实现一些特殊功能。例如实现URL级别的权限访问控制、过滤敏感词汇、压缩响应信息等一些功能。
2、Filter如何实现拦截
web浏览器<————>web服务器<————>过滤器<————>web资源
Filter接口中有一个doFilter方法,web服务器在调用doFilter方法时,会传递一个filterChain对象,该对象有一个doFilter()方法,如果调用改方法,则服务器调用web资源,否则web资源不会被访问。
Filter接口的doFilter()方法:
- 前:对request,response做预处理
- 是否调用目标资源,是否执行filterChain对象的diFilter()方法。
- 后:捕获响应的web资源,实现一些新的功能。
3、Filter开发步骤
Filter开发分为两个步骤,首先要编写java类实现filter接口和doFilter方法,二是在web.xml中对编写的filter接口进行注册和映射,设置其拦截的资源。
3.1 XML配置
3.1.1 filter注册
注册Filter范例:
<filter>
<description>FilterDemo02过滤器</description>
<filter-name>FilterDemo02</filter-name>
<filter-class>me.gacl.web.filter.FilterDemo02</filter-class>
<!--配置FilterDemo02过滤器的初始化参数-->
<init-param>
<description>配置FilterDemo02过滤器的初始化参数</description>
<param-name>name</param-name>
<param-value>gacl</param-value>
</init-param>
<init-param>
<description>配置FilterDemo02过滤器的初始化参数</description>
<param-name>like</param-name>
<param-value>java</param-value>
</init-param>
</filter>
-
标签用于内注册一个filter过滤器<filter>
-
标签用于添加描述信息(可省略)<description>
-
标签用于指定过滤器的名称<filter-name>
-
标签用于指定过滤器的类名<filter-class>
-
标签用于设置filter的初始化参数,<init-param>
和<param-name>
制定了某属性的名称及该属性的值。<param-value>
<init-param>
标签中的属性可以通过FilterConfig对象的方法获得。
3.1.2 filter映射
映射filter实例1:
<!--映射过滤器-->
<filter-mapping>
<filter-name>FilterDemo02</filter-name>
<!--“/*”表示拦截所有的请求 -->
<url-pattern>/*</url-pattern>
</filter-mapping>
映射filter实例2:
<filter-mapping>
<filter-name>testFilter</filter-name>
<url-pattern>/index.jsp</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
</filter-mapping>
<dispatcher>
标签设置用户以什么方式访问时,过滤器工作。标签的属性值:
- REQUEST:用户直接访问页面,过滤器工作。
- INCLUDE:目标资源通过RequestDispatcher的include()方法访问,过滤器工作。
- FORWARD:目标资源通过RequestDispatcher的forward()方法访问,过滤器工作。
- ERROR:目标资源通过声明式异常处理机制调用时,过滤器工作。
3.2 实现Filter接口并实现doFilter方法
过滤器范例:
public class FilterDemo01 implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("----过滤器初始化----");
}
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
//对request和response进行一些预处理
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=UTF-8");
System.out.println("FilterDemo01执行前!!!");
chain.doFilter(request, response); //让目标资源执行,放行
System.out.println("FilterDemo01执行后!!!");
}
@Override
public void destroy() {
System.out.println("----过滤器销毁----");
}
}
4、Filter的生命周期
Filter的创建和销毁都由WEB服务器负责。web应用程序启动,web服务器创建Filter的实例对象,调用init方法,这个过程只执行一次。
Filter的销毁由web容器调用destroy方法销毁Filter,释放过滤器使用的资源。在Filter的生命周期内执行一次。
5、Decorator设计模式
5.1 Decorator模式介绍
在某个对象不能满足业务需求时,通常有两种方式来对其进行增强:
- 编写子类,继承并重写需要增强的方法。
- 使用Decorator设计模式对方法进行增强。
一般情况下我们使用第一种方法,但在某个特殊情况下,我们只能使用第二种方法,即:被增强的对象,只能得到对象,不能得到class文件。
在filter中可以得到request,response对象,可以使用decorator模式对request和response对象进行包装, 在把包装对象传给目标资源。
5.2 Decorator模式实现
- 被增强对象集成了什么接口和父类,编写一个类,也继承这些接口和父类。
- 在类中定义变量,变量类型即被增强对象的类型。
- 在类中定义构造函数,接收被增强对象。
- 覆盖需增强的方法,编写增强的代码。
Servlet API中提供了一个request对象的Decorator设计模式的默认实现类HttpServletRequestWrapper,(HttpServletRequestWrapper类实现了request接口中的所有方法,但这些方法的内部实现都是仅仅调用了一下所包装的request对象的对应方法)以避免用户在对request对象进行增强时需要实现request接口中的所有方法。
所以当需要增强request对象时,只需要写一个类继承HttpServletRequestWrapper类,然后在重写需要增强的方法即可
当需要增强response对象时,继承HttpServletResponseWrapper类。
6、Filter的应用
6.1 统一全站字符编码
public class EncodingFilter implements Filter {
String encoding = null;
FilterConfig filterConfig = null;
public void init(FilterConfig config ) throws ServletException{
this.filterConfig=config;
this.encoding=filterConfig.getInitParameter("encoding");
}
public void doFilter(ServletRequest request , ServletResponse response , FilterChain chain)
throws IOException , ServletException{
if(encoding !=null){
request.setCharacterEncoding(encoding);
}
chain.doFilter(request, response);
}
public void destroy(){
this.encoding = null;
this.filterConfig = null;
}
}
在init方法中获得XML注册时的encoding属性值,将它传递给该类的属性encoding,在doFilter方法中,使用erquest.setCharacterEncoding()方法,设置request的属性值。
6.2 禁止浏览器缓存动态页面
public class NoCacheFilter implements Filter{
public void init (){}
public void doFilter(ServletRequest req , ServletResponse resp, FliterChain chain)
throws IOException ,ServletException{
//强制转换
HttpServletRequest request = (HttpServletRequest )req;
HttpServletResponse response = (HttpServletResponse ) resp;
//三种方法设置禁止缓存
response.setDataHeader("Expires",-1);
response.setHeader("Cache-control","no-cache");
response.setHeader("Pragma","no-cache");
chain.doFilter(request,response);
}
public void destroy(){}
}
XML配置:
<filter>
<filter-name>NoCacheFilter</filter-name>
<filter-class>web.filter.NoCacheFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>NoCacheFilter</filter-name>
<!--只拦截Jsp请求-->
<servlet-name>*.jsp</servlet-name>
</filter-mapping>
HTTP响应头字段有三种禁止浏览器缓存的头字段:
- Expires:告诉浏览器把回送的资源缓存多长时间 -1或0则是不缓存
- Cache-Control:no-cache,控制浏览器不要缓存数据
- Pragma:no-cache,控制浏览器不要缓存数据
HTTP请求中的常用请求字段和HTTP的响应状态码及响应头
http://blog.csdn.net/qxs965266509/article/details/8082810
6.3 控制浏览器缓存静态页面
public class NoCacheFilter implements Filter{
public void init (){}
public void doFilter(ServletRequest req , ServletResponse resp, FliterChain chain)
throws IOException ,ServletException{
//强制转换
HttpServletRequest request = (HttpServletRequest )req;
HttpServletResponse response = (HttpServletResponse ) resp;
//得到请求资源的rul
String url = request.getRequestURI();
//得到请求资源的后缀
String ext = uri.substring(uri.lastIndexOf(".")+1);
//得到该后缀在xml设置中的缓存时间属性值
String time = filterConfig.getInitParameter(ext);
if(time!=null){
long t = Long.parseLong(time)*3600*1000;
//设置响应头字段Expires的缓存时间
response.setDataHeader("Expires",System.currentTimeMillis()+t);
}
}
public void destroy(){}
}
XML配置:
<!-- 配置缓存过滤器 -->
<filter>
<filter-name>CacheFilter</filter-name>
<filter-class>me.gacl.web.filter.CacheFilter</filter-class>
<!-- 配置要缓存的web资源以及缓存时间,以小时为单位 -->
<init-param>
<param-name>css</param-name>
<param-value>4</param-value>
</init-param>
<init-param>
<param-name>jpg</param-name>
<param-value>1</param-value>
</init-param>
<init-param>
<param-name>js</param-name>
<param-value>4</param-value>
</init-param>
<init-param>
<param-name>png</param-name>
<param-value>4</param-value>
</init-param>
</filter>
<!-- 配置要缓存的web资源的后缀-->
<filter-mapping>
<filter-name>CacheFilter</filter-name>
<url-pattern>*.jpg</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>CacheFilter</filter-name>
<url-pattern>*.css</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>CacheFilter</filter-name>
<url-pattern>*.js</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>CacheFilter</filter-name>
<url-pattern>*.png</url-pattern>
</filter-mapping>
6.4 用户自动登陆
引用redarmychen的部分代码
实现用户自动登陆的思路:
- 用户首次登陆成功后,发送一个名称为user的cookie给客户端,cookie的值为用户名和加密的密码,同时在session对象中存放一个用户登录记录的属性值。
- 编写一个LoginFilter,filter检查session的属性值是否匹配,或者检查用户是否带有名称为user的cookie,(如果有,则调用dao查询cookie的用户名和密码是否和数据库匹配)以实现程序完成自动登陆。
LoginServlet的部分代码
创建cookie:
long time = System.currentTimeMillis() + expires * 1000;
//cookie拼接的value值,(可以根据自己的想法设计)
String cookieValue = name + ":" + time + ":" + md5Value(name + ":" + pass + ":" + time);
//创建cookie
autoCookie = new Cookie("autologin", cookieValue);
添加cookie:
autoCookie.setMaxAge((int) expires);
autoCookie.setPath("/day56");
// 添加cookie
response.addCookie(autoCookie);
添加session对象:
request.getSession().setAttribute("admin", entity);
LoginFilter的部分代码
查看session中是否有标志:
// 1.首先判断sesion中有没有admin
Object object = request.getSession().getAttribute("admin");
// 如果session中有用户
if (object != null) {
// 跳转到成功登录的界面
request.getRequestDispatcher("./sc.jsp").forward(request, response);
return;
}
查看cookie中是否有用户登录记录:
// 2.判断cookie中是否存在 autologin标识符 的cookie对象
// 声明cookie
Cookie autoCookie = null;
// 获取所有的cookie
Cookie cookies[] = request.getCookies();
// 如果没有cookie信息,就继续执行login.do,跳转到login.jsp页面
if (cookies != null) {
// 如果有,就遍历cookie
for (Cookie cookie : cookies) {
// 判断cookie中是否有autologin标识符的cookie
if ("autologin".equals(cookie.getName())) {
autoCookie = cookie; // 如果有 就赋值给临时变量autoCookie
}
}
// 3. 判断autoCookie是否等于null
if (autoCookie == null) {
// 如果等于null,则继续执行login.jsp页面
chain.doFilter(request, response);
return;
}
// 3.如果autoCookie不等于null,就判断cookie的值
// 获取cookie值
String value = autoCookie.getValue();
// 拆分cookie的值
String temp[] = value.split(":");
System.out.println(temp.length);
// 判断长度 是否等于自己拼接的长度
if (temp.length != 3) {
// 如果不等于3,则继续执行login.jsp页面
chain.doFilter(request, response);
return;
}
// 获取cookie拆分的各个值
String name = temp[0]; // 用户名
String time = temp[1];// 获取有效时间
String service_md5Value = temp[2];// 获取md5的加密后的字符
// 4.判断cookie是否失效
if (Long.valueOf(time) <= System.currentTimeMillis()) {
// 如果失效,则继续执行login.jsp页面
chain.doFilter(request, response);
return;
}
// 5.如果cookie没有失效,根据用户名,去查询用户信息
AdminService adminService = new AdminServiceImpl();
// 查询用户信息
Admin entity = adminService.checkLogin(name);
System.out.println(entity+"0000");
// 判断用户是否为null
if (entity == null) {
// 如果没有查询的用户,则继续执行login.jsp页面
chain.doFilter(request, response);
return;
}
// 按照服务器拼接的字符的方式,拼接md5加密的字符串
String md5Temp = entity.getName() + ":" + entity.getPass() + ":"
+ time;
// 判断md5加密后和服务器端加密的字符是否相等
if (!(md5Value(md5Temp).equals(service_md5Value))) {
// 在不相等的情况下,则继续执行login.jsp页面
chain.doFilter(request, response);
return;
}
// 如果满足了cookie取值判断的所有结果,则跳转到成功登录的界面.
request.getSession().setAttribute("admin", entity);
request.getRequestDispatcher("./sc.jsp").forward(request, response);
} else {
// 在没有cookie信息的时候,则继续login.jsp页面
chain.doFilter(request, response);
return;
}
}