欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Servlet 知识详解(四)之 —— Filter对象 过滤器 学习笔记

程序员文章站 2022-04-21 09:26:49
...

 

本文查阅方法:
    1、查阅目录 —— 查阅本文目录,确定想要查阅的目录标题
    2、快捷“查找” —— 在当前浏览器页面,按键 “Ctrl+F” 按键组合,开启浏览器的查找功能,
             在查找搜索框中 输入需要查阅的 目录标题,便可以直接到达 标题内容 的位置。
    3、学习小结 —— 文中的学习小结内容,是笔者在学习之后总结出的,开发时可直接参考其进行应用开发的内容, 进一步加快了本文的查阅 速度。(水平有限,仅供参考。)

 

 

 


 

 

 

本文目录

 

 学习小结

 

1、Filter技术 简介

 

2、Filter是如何实现拦截的?

 

3、Filter开发入门

 

4、Filter的生命周期

 

5、FilterConfig接口

 

6、Filter在开发中的常见应用的思想:

 

7、Filter常见应用(1)——统一全站字符编码的过滤器(解决中文乱码)

 

8、Filter常见应用(2)——控制浏览器缓存页面中的静态资源的过滤器

 

9、Filter常见应用(3)——禁止浏览器缓存所有动态页面的过滤器:

 

10、Filter常见应用(4)——使用Filter实现URL级别的权限认证

 

11、Filter常见应用(5)——实现用户自动登陆的过滤器

 

12、Filter的部署—注册Filter

 

13、Filter的部署—映射Filter

 

14、Filter映射——<dispatcher> 子元素可以设置的值及其意义

 

15、Filter高级开发

 

16、Decorator设计模式

 

17、Decorator设计模式的实现步骤

 

18、request对象的增强

 

19、request对象的增强案例

 

20、敏感词过滤器——过滤敏感词汇(禁用、审核、替换三种)

 

21、response对象的增强

 

22、response增强案例—压缩响应

 

23、实用案例-缓存数据到内存

 

 

 

    《Servlet 知识详解(一)之  ——  ServletContext对象 和 ServletConfig对象 学习笔记》

 

        地址: http://even2012.iteye.com/blog/1838063

 

    《Servlet 知识详解(二)之  ——  Request对象 和 Response对象 学习笔记》

 

        地址:http://even2012.iteye.com/blog/1838099

 

    《Servlet 知识详解(三)之  ——  Cookie对象 和 Session对象  学习笔记》

 

        地址:http://even2012.iteye.com/blog/1838128

    《Servlet 知识详解(四)之  ——  Filter对象 过滤器  学习笔记》

 

        地址:http://even2012.iteye.com/blog/1963466

    《Servlet 知识详解(五)之  ——  Listener对象 监听器  学习笔记》

 

        地址:http://even2012.iteye.com/blog/1963467

 

 


 

学习小结

 

 

 

 


 

 

 

1、Filter技术 简介

 

        Filter也称之为过滤器,它是Servlet技术中最激动人心的技术,WEB开发人员通过Filter技术,对web服务器管理的所有web资源:例如Jsp, Servlet, 静态图片文件或静态 html 文件等进行拦截,从而实现一些特殊的功能。例如实现URL级别的权限访问控制、过滤敏感词汇、压缩响应信息等一些高级功能。

 

        Servlet API中提供了一个Filter接口,开发web应用时,如果编写的Java类实现了这个接口,则把这个java类称之为过滤器Filter。通过Filter技术,开发人员可以实现用户在访问某个目标资源之前,对访问的请求和响应进行拦截,如下所示:

 


Servlet 知识详解(四)之  ——  Filter对象 过滤器  学习笔记
            
    
    博客分类: 学习笔记 Filter过滤器JavaServlet拦截请求 
 

 

 

 


 

 

 

2、Filter是如何实现拦截的?

 

        Filter接口中有一个doFilter方法,当开发人员编写好Filter,并配置对哪个web资源进行拦截后,WEB服务器每次在调用web资源的service方法之前,都会先调用一下filter的doFilter方法,因此,在该方法内编写代码可达到如下目的:

 

        (1) 调用目标资源之前,让一段代码执行

 

        (2) 是否调用目标资源(即是否让用户访问web资源)。

 

               拦截原理:web服务器在调用doFilter方法时,会传递一个filterChain对象进来,filterChain对象是filter接口中最重要的一个对象,它也提供了一个doFilter方法,开发人员可以根据需求决定是否调用此方法,调用该方法,则web服务器就会调用web资源的service方法,即web资源就会被访问,否则web资源不会被访问。

 

        (3) 调用目标资源之后,让一段代码执行

 

        

 


 

 

 

3、Filter开发入门

 

        Filter开发分为二个步骤:

 

            (1) 编写java类实现Filter接口,并实现其doFilter方法。

 

            (2) 在 web.xml 文件中使用<filter>和<filter-mapping>元素对编写的filter类进行注册,并设置它所能拦截的资源。

 

        Filter链介绍

 

            (1) 在一个web应用中,可以开发编写多个Filter,这些Filter组合起来称之为一个Filter链。

 

            (2) web服务器根据Filter在web.xml文件中的注册顺序,决定先调用哪个Filter,当第一个Filter的doFilter方法被调用时,web服务器会创建一个代表Filter链的FilterChain对象传递给该方法。在doFilter方法中,开发人员如果调用了FilterChain对象的doFilter方法,则web服务器会检查FilterChain对象中是否还有filter,如果有,则调用第2个filter,如果没有,则调用目标资源。

 

            (3) Filter链实验(查看FilterChain API文档)

 

 

 


 

 

 

4、Filter的生命周期

 

    创建:init(FilterConfig filterConfig)throws ServletException:

 

        (1) 和我们编写的Servlet程序一样,Filter的创建和销毁由WEB服务器负责。 web 应用程序启动时,web 服务器将创建Filter 的实例对象,并调用其init方法,完成对象的初始化功能,从而为后续的用户请求作好拦截的准备工作(注:filter对象只会创建一次,init方法也只会执行一次。)

 

        (2) 开发人员通过init方法的参数,可获得代表当前filter配置信息的FilterConfig对象。

 

    销毁:destroy():

 

        在Web容器卸载 Filter 对象之前被调用。该方法在Filter的生命周期中仅执行一次。在这个方法中,可以释放过滤器使用的资源。(Filter对象的销毁同Servlet一样:关闭Web容器或者卸载当前Web应用两种情况)

 

        

 


 

 

 

5、FilterConfig接口

 

     用户在配置filter时,可以使用<init-param>为filter配置一些初始化参数,当web容器实例化Filter对象,调用其init方法时,会把封装了filter初始化参数的filterConfig对象传递进来。因此开发人员在编写filter时,通过filterConfig对象的方法,就可获得:

 

        (1) String getFilterName():得到filter的名称。

 

        (2) String getInitParameter(String name): 返回在部署描述中指定名称的初始化参数的值。如果不存在返回null.

 

        (3) Enumeration getInitParameterNames():返回过滤器的所有初始化参数的名字的枚举集合。

 

        (4) public ServletContext getServletContext():返回Servlet上下文对象的引用。

 

        

 

Demo样例:Filter文件 : 

 

public class FilterDemo1 implements Filter {

 

  private FilterConfig config;

 

 

 

  public void doFilter(ServletRequest request, ServletResponse response,

 

      FilterChain chain) throws IOException, ServletException {

 

 

 

    //从Filter配置信息的初始化参数中 取出设定的字符集参数的值。

 

    String charEnc = this.config.getInitParameter("charEnc");

 

 

 

    //将取出的字符集用于设定 全站的字符编码设定。

 

    request.setCharacterEncoding(charEnc);

 

    response.setCharacterEncoding(charEnc);

 

    response.setContentType("text/html;charset="+charEnc);

 

 

 

    System.out.println("servletDemo1之前");    

 

    chain.doFilter(request, response);  //放行    

 

    System.out.println("servletDemo1之后!!");

 

  }

 

 

 

  public void init(FilterConfig filterConfig) throws ServletException {

 

    System.out.println("filter被创建了!!");

 

    this.config = filterConfig;

 

  }

 

 

 

  public void destroy() {

 

    System.out.println("filter被销毁了!!");

 

  }

 

}

 

 

 

web.xml文件

 

<filter>

 

    <filter-name>FilterDemo1</filter-name>

 

    <filter-class>cn.itcast.web.filter.FilterDemo1</filter-class>

 

    <init-param>

 

      <param-name>charEnc</param-name>

 

      <param-value>UTF-8</param-value>

 

    </init-param>

 

  </filter>

 

<filter-mapping>

 

    <filter-name>FilterDemo1</filter-name>

 

    <url-pattern>/*</url-pattern>

 

  </filter-mapping>

 

 

 


 

 

 

6、Filter在开发中的常见应用的思想:

 

     (1) filter可以目标资源执行之前,进行权限检查,检查用户有无权限,如有权限则放行,如没有,则拒绝访问

 

     (2) filter可以放行之前,对request和response进行预处理,从而实现一些全局性的设置。

 

     (3) filter在放行之后,可以捕获到目标资源的输出,从而对输出作出类似于压缩这样的设置

 

 

 


 

 

 

7、Filter常见应用(1)——统一全站字符编码的过滤器(解决中文乱码)

 

    通过配置参数encoding指明使用何种字符编码,以处理Html Form请求参数的中文问题

 

Demo样例1:Filter类 

 

//真正解决全站乱码(Post 和 Get 方式 全部解决)

 

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");

 

  }

 

 

 

  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);

 

      }      

 

    }    

 

  }

 

 

 

  public void destroy() {

 

    // TODO Auto-generated method stub

 

  }

 

 

 

  public void init(FilterConfig filterConfig) throws ServletException {

 

  }

 

}

 

 

 

Demo样例2:web.xml文件

 

<filter>

 

    <filter-name>CharacterEncodingFilter2</filter-name>

 

    <filter-class>cn.itcast.web.filter.example.CharacterEncodingFilter2</filter-class>

 

  </filter>

 

 

 

  <filter-mapping>

 

    <filter-name>CharacterEncodingFilter2</filter-name>

 

    <url-pattern>/*</url-pattern>

 

  </filter-mapping>

 

 

 


 

 

 

 

 

8、Filter常见应用(2)——控制浏览器缓存页面中的静态资源的过滤器

 

场景:有些动态页面中引用了一些图片或css文件以修饰页面效果,这些图片和css文件经常是不变化的,所以为减轻服务器的压力,可以使用filter控制浏览器缓存这些文件,以提升服务器的性能。

 

 

 

Demo样例1:Filter类 : 

 

//控制浏览器缓存的过滤器

 

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);

 

  }

 

 

 

  public void init(FilterConfig filterConfig) throws ServletException {

 

    this.config = filterConfig;

 

  }

 

 

 

  public void destroy() {

 

 

 

  }

 

}

 

 

 

Demo样例2:web.xml文件 :

 

<filter>

 

    <filter-name>CacheFilter</filter-name>

 

    <filter-class>cn.itcast.web.filter.example.CacheFilter</filter-class>

 

    <init-param>

 

      <param-name>css</param-name>

 

      <param-value>10</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>20</param-value>

 

    </init-param>

 

  </filter> 

 

 

 

  <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>

 

  

 


 

  

 

9、Filter常见应用(3)——禁止浏览器缓存所有动态页面的过滤器:

 

    (1) 有 3 个 HTTP 响应头字段都可以禁止浏览器缓存当前页面,它们在 Servlet 中的示例代码如下:

 

        (a) response.setDateHeader("Expires",-1);

 

        (b) response.setHeader("Cache-Control","no-cache"); 

 

        (c) response.setHeader("Pragma","no-cache"); 

 

    (2) 并不是所有的浏览器都能完全支持上面的三个响应头,因此最好是同时使用上面的三个响应头。

 

    (3) Expires数据头:值为GMT时间值,为-1指浏览器不要缓存页面

 

    (4) Cache-Control响应头有两个常用值: 

 

        (a) no-cache指浏览器不要缓存当前页面。

 

        (b) max-age:xxx指浏览器缓存页面xxx秒。

 

【备注:此例较为简单,Demo样例只要按照上例《8、Filter常见应用(2)——控制浏览器缓存页面中的静态资源的过滤器 》 即可实现。】

 

Demo样例1:Filter类 

 

         参见《8、Filter常见应用(2)——控制浏览器缓存页面中的静态资源的过滤器 》

 

Demo样例2:web.xml文件 

 

         参见《8、Filter常见应用(2)——控制浏览器缓存页面中的静态资源的过滤器 》

 

Demo样例3:Service类   

 

         参见《8、Filter常见应用(2)——控制浏览器缓存页面中的静态资源的过滤器 》

 

 

 


 

  

 

10、Filter常见应用(4)——使用Filter实现URL级别的权限认证

 

情景:在实际开发中我们经常把一些执行敏感操作的servlet映射到一些特殊目录中,并用filter把这些特殊目录保护起来,限制只能拥有相应访问权限的用户才能访问这些目录下的资源。从而在我们系统中实现一种URL级别的权限功能。

 

要求:为使Filter具有通用性,Filter保护的资源和相应的访问权限通过filter参数的形式予以配置。

 

 

 

Demo样例1:Filter类 

 

 

 

Demo样例2:web.xml文件

 

 

 

Demo样例3:Service类  

 

 

 

 

 

 


 

  

 

11、Filter常见应用(5)——实现用户自动登陆的过滤器

 

       (1) 在用户登陆成功后,发送一个名称为user的cookie给客户端,cookie的值为用户名和md5加密后的密码。

 

       (2) 编写一个AutoLoginFilter,这个filter检查用户是否带有名称为user的cookie来,如果有,则调用dao查询cookie的用户名和密码是否和数据库匹配,匹配则向session中存入user对象(即用户登陆标记),以实现程序完成自动登陆。

 

         

 

Demo样例1:Filter类  

 

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.没登陆,再执行自动登陆逻辑

 

 

 

    //(1)看用户有没有带自动登陆的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;

 

    }

 

 

 

    //(2)用户带了自动登陆的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;

 

    }

 

 

 

    //(3)代表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;

 

    }

 

    //发挥浏览器Cookie的格式: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;

 

    }

 

 

 

    //(4)执行登陆

 

    request.getSession().setAttribute("user", user);

 

    chain.doFilter(request, response);    

 

  }

 

 

 

  //MD5加密算法

 

  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);

 

    }

 

  }  

 

 

 

  public void destroy() {

 

    // TODO Auto-generated method stub

 

  }

 

 

 

  public void init(FilterConfig filterConfig) throws ServletException {

 

    // TODO Auto-generated method stub

 

  }

 

}

 

 

 

Demo样例2:web.xml文件 

 

  <filter>

 

    <filter-name>AutoLoginFilter</filter-name>

 

    <filter-class>cn.itcast.web.filter.example.AutoLoginFilter</filter-class>

 

  </filter>  

 

 

 

  <filter-mapping>

 

    <filter-name>AutoLoginFilter</filter-name>

 

    <url-pattern>/*</url-pattern>

 

  </filter-mapping>

 

 

 

Demo样例3:Servlet类  

 

public class LoginServlet extends HttpServlet { 

 

  public void doGet(HttpServletRequest request, HttpServletResponse response)

 

      throws ServletException, IOException {

 

 

 

    String username = request.getParameter("username");

 

    String password = request.getParameter("password");

 

 

 

    BusinessService service = new BusinessService();

 

    User user = service.login(username, password);

 

    if(user==null){

 

      request.setAttribute("message", "用户名或密码错误!!");

 

      request.getRequestDispatcher("/message.jsp").forward(request, response);

 

      return;

 

    }

 

 

 

    request.getSession().setAttribute("user", user);

 

    int expirestime = Integer.parseInt(request.getParameter("time"));

 

    //给客户机发送自动登陆的 cookie  

 

    //autologin=username:expirestime:md5(password:expirestime:username)

 

    Cookie cookie = makeCookie(user, expirestime);

 

    response.addCookie(cookie);

 

    response.sendRedirect("/day19/index.jsp");

 

  }

 

 

 

  //生成发回给浏览器的Cookie

 

  public Cookie makeCookie(User user,int expirestime){

 

    long currenttime = System.currentTimeMillis();

 

    String cookieValue = user.getUsername() + ":" + (currenttime+expirestime*1000) + ":" + md5(user.getUsername(), user.getPassword(), (currenttime+expirestime*1000));

 

    Cookie cookie = new Cookie("autologin",cookieValue);

 

    cookie.setMaxAge(expirestime);

 

    cookie.setPath("/day19");

 

    return cookie;

 

  }

 

 

 

  //MD5 加密算法

 

  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);

 

    }

 

  }

 

 

 

  public void doPost(HttpServletRequest request, HttpServletResponse response)

 

      throws ServletException, IOException {

 

    doGet(request, response);

 

  }

 

}

 

 

 

Demo样例4:Service类 

 

public class BusinessService { 

 

  private static List<User> list = new ArrayList();

 

  static{

 

    list.add(new User("aaa","123"));

 

    list.add(new User("bbb","123"));

 

    list.add(new User("ccc","123"));

 

  }

 

 

 

  public User login(String username,String password){

 

    for(User user : list){

 

      if(user.getUsername().equals(username) && user.getPassword().equals(password)){

 

        return user;

 

      }

 

    }

 

    return null;

 

  }

 

 

 

  public User findUser(String username){

 

    for(User user : list){

 

      if(user.getUsername().equals(username)){

 

        return user;

 

      }

 

    }

 

    return null;

 

  }  

 

}

 

 

 

Demo样例5:User类  

 

 

 

 

 


 

 

 

12、Filter的部署—注册Filter

 

Demo样例:

 

<filter>

 

        <filter-name>testFitler</filter-name>

 

       <filter-class>org.test.TestFiter</filter-class>

 

       <init-param>

 

             <param-name>word_file</param-name>  

 

             <param-value>/WEB-INF/word.txt</param-value>

 

       </init-param>

 

</filter>

 

 详解:

 

        (1) <filter-name>用于为过滤器指定一个名字,该元素的内容不能为空。

 

        (2) <filter-class>元素用于指定过滤器的完整的限定类名。

 

        (3) <init-param>元素用于为过滤器指定初始化参数,它的子元素<param-name>指定参数的名字,<param-value>指定参数的值。在过滤器中,可以使用FilterConfig接口对象来访问初始化参数。

 

 

 


 

  

 

13、Filter的部署—映射Filter

 

    <filter-mapping>元素用于设置一个 Filter 所负责拦截的资源。一个Filter拦截的资源可通过两种方式来指定:Servlet 名称和资源访问的请求路径

 

        (1) <filter-name>子元素用于设置filter的注册名称。该值必须是在<filter>元素中声明过的过滤器<filter-name> 的名字

 

        (2) <url-pattern>设置 filter 所拦截的请求路径(过滤器关联的URL样式:规则同Servlet中的相同)

 

        (3) <servlet-name>指定过滤器所拦截的Servlet名称。(比如:缓存分类信息时,拦截查询数据库分类信息的servlet)

 

        (4) <dispatcher>指定过滤器所拦截的资源被 Servlet 容器调用的方式,可以是REQUEST,INCLUDE,FORWARD和ERROR之一,默认REQUEST。用户可以设置多个<dispatcher> 子元素用来指定 Filter 对资源的多种调用方式进行拦截。

 

 

 

    Filter的部署—映射Filter示例

 

    <filter-mapping>

 

         <filter-name>testFilter</filter-name>

 

        <url-pattern>/test.jsp</url-pattern>

 

    </filter-mapping> 

 

     

 

    <filter-mapping>

 

        <filter-name>testFilter</filter-name>

 

       <url-pattern>/index.jsp</url-pattern>

 

       <dispatcher>REQUEST</dispatcher>

 

       <dispatcher>FORWARD</dispatcher>

 

    </filter-mapping>

 

 

 

 


 

 

 

 

 

14、Filter映射——<dispatcher> 子元素可以设置的值及其意义

 

        (1) REQUEST:当用户直接访问页面时,Web容器将会调用过滤器。如果目标资源是通过RequestDispatcher的include()或forward()方法访问时,那么该过滤器就不会被调用。

 

        (2) INCLUDE:如果目标资源是通过RequestDispatcher的include()方法访问时,那么该过滤器将被调用。除此之外,该过滤器不会被调用。

 

        (3) FORWARD:如果目标资源是通过RequestDispatcher的forward()方法访问时,那么该过滤器将被调用,除此之外,该过滤器不会被调用。

 

        (4) ERROR:如果目标资源是通过“声明式异常处理机制”调用时,那么该过滤器将被调用。除此之外,过滤器不会被调用。(Jsp页面的错误页面设定机制)         

 

【备注:其实四种调用方式的每种指令都是由Servlet先反馈给Web服务器,再由Web服务器根据反馈指令所要求的方式去调用指定的类。】

 

 

 


Servlet 知识详解(四)之  ——  Filter对象 过滤器  学习笔记
            
    
    博客分类: 学习笔记 Filter过滤器JavaServlet拦截请求 
 
  

 


 

 

 

15、Filter高级开发

 

        由于开发人员在filter中可以得到代表用户请求和响应的request、response对象,因此在编程中可以使用Decorator(装饰器)模式对request、response对象进行包装,再把包装对象传给目标资源,从而实现一些特殊需求。

 

 

 


 

 

 

16、Decorator设计模式

 

    (1) 当某个对象的方法不适应业务需求时,通常有2种方式可以对方法进行增强:

 

            (a) 编写子类,覆盖需增强的方法

 

            (b) 使用Decorator设计模式对方法进行增强

 

    (2) 疑问:在实际应用中遇到需增强对象的方法时,到底选用哪种方式呢?

 

            (a) 没有具体的定式,不过有一种情况下,必须使用Decorator设计模式:即被增强的对象,开发人员只能得到它的对象,无法得到它的class文件。

 

            (b) 比如request、response对象,开发人员之所以在servlet中能通过sun公司定义的HttpServletRequest\response接口去操作这些对象,是因为Tomcat服务器厂商编写了request、response接口的实现类。web服务器在调用servlet时,会用这些接口的实现类创建出对象,然后传递给servlet程序。

 

            (c) 此种情况下,由于开发人员根本不知道服务器厂商编写的request、response接口的实现类是哪个?在程序中只能拿到服务器厂商提供的对象,因此就只能采用Decorator设计模式对这些对象进行增强。

 

 

 


 

 

 

17、Decorator设计模式的实现步骤

 

        (1) 首先看需要被增强对象继承了什么接口或父类,编写一个类也去继承这些接口或父类。

 

        (2) 在类中定义一个变量,变量类型即需增强对象的类型。

 

        (3) 在类中定义一个构造函数,接收需增强的对象。

 

        (4) 覆盖需增强的方法,编写增强的代码。

 

        (5) 实现接口中其他的方式,并用被增强对象调用相同方法以返回结果。

 

 

 


 

 

 

18、request对象的增强

 

        Servlet API 中提供了一个request对象的Decorator设计模式的默认实现类HttpServletRequestWrapper , (HttpServletRequestWrapper 类实现了request 接口中的所有方法,但这些方法的内部实现都是仅仅调用了一下所包装的的 request 对象的对应方法)以避免用户在对request对象进行增强时需要实现request接口中的所有方法。

 

 

 


 

 

 

19、request对象的增强案例

 

        (1) 使用Decorator模式包装request对象,完全解决get、post请求方式下的中文乱码问题。 (Demo参见标题7、Filter常见应用(1)——统一全站字符编码的过滤器(解决中文乱码) 中的Demo样例)

 

        (2) 使用Decorator模式包装request对象,实现html标签转义功能(Tomcat服务器中提供了转义html标签的工具类)。

 

【Html转义:方式浏览器表单被恶意提交脚本代码,当被再次使用浏览器浏览时,就是执行这些脚本代码从而对用户造成 损害。如意:死循环弹出确认对话框】

 

 

 

Demo样例1:Filter类  

 

public class HtmlFilter implements Filter {

 

  public void destroy() {

 

    // TODO Auto-generated method stub

 

  }

 

 

 

  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>

 

  }

 

 

 

  装饰模式 增强 request的功能——实现Html转义功能。

 

  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);

 

    }

 

 

 

    //执行Html转义的方法

 

    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("&lt;");

 

                  break;

 

              case '>':

 

                  result.append("&gt;");

 

                  break;

 

              case '&':

 

                  result.append("&amp;");

 

                  break;

 

              case '"':

 

                  result.append("&quot;");

 

                  break;

 

              default:

 

                  result.append(content[i]);

 

              }

 

          }

 

          return (result.toString());

 

      }    

 

  }

 

 

 

  public void init(FilterConfig filterConfig) throws ServletException {

 

    // TODO Auto-generated method stub

 

  }

 

}

 

 

 

Demo样例2:web.xml文件

 

<filter>

 

    <filter-name>HtmlFilter</filter-name>

 

    <filter-class>cn.itcast.web.filter.example.HtmlFilter</filter-class>

 

  </filter> 

 

 

 

  <filter-mapping>

 

    <filter-name>HtmlFilter</filter-name>

 

    <url-pattern>/*</url-pattern>

 

  </filter-mapping>

 

 

 

 


 

 

 

 20、敏感词过滤器——过滤敏感词汇(禁用、审核、替换三种)

 

实际开发中我们需要对用户提交的言论,进行敏感词的过滤,否则会触动监管部门。

 

敏感词汇 可分为禁用、审核、替换三种

 

思路:

 

1、读取词库——将敏感词汇库文件分类读取到这三种词汇集合中(三个List集合)

 

2、匹配——使用正则表达式,将三个集合中的每个词汇都取出来到用户提交的内容中进行匹配,

 

        (1)若是有禁用词汇,直接告诉用户含有敏感词,请修改后再提交,不存入数据库;

 

        (2)若是有审核词汇,则将审核词汇进行高亮修饰后,再存入数据库,由管理员进行查阅审核。

 

        (3)若是有替代词,则直接使用预定的符号或者词汇,进行替换,然后存入数据库。

 

  

 

Demo样例:Filter.java过滤器内容

 

public class WordsFilter implements Filter {

 

 

 

  private List<String> banWords = new ArrayList();

 

  private List<String> auditWords = new ArrayList();

 

  private List<String> replaceWords = new ArrayList();

 

 

 

  //在过滤器初始化的时候就将词汇库读入到三个集合中。

 

  public void init(FilterConfig filterConfig) throws ServletException {

 

    try{

 

      String path = WordsFilter.class.getClassLoader().getResource("cn/itcast/words").getPath();

 

      File files[] =  new File(path).listFiles();

 

      for(File file : files){

 

        if(!file.getName().endsWith(".txt")){

 

          continue;

 

        }

 

        BufferedReader br = new BufferedReader(new FileReader(file));

 

        String line = null;

 

        while((line=br.readLine())!=null){

 

          String s[] = line.split("\\|");

 

          if(s.length!=2){

 

            continue;

 

          }

 

          if(s[1].trim().equals("1")){

 

            banWords.add(s[0].trim());

 

          }

 

 

 

          if(s[1].trim().equals("2")){

 

            auditWords.add(s[0].trim());

 

          }

 

 

 

          if(s[1].trim().equals("3")){

 

            replaceWords.add(s[0].trim());

 

          }

 

        }

 

      }

 

      System.out.println("haha");

 

    }catch (Exception e) {

 

      throw new RuntimeException(e);

 

    }

 

  }

 

 

 

  public void doFilter(ServletRequest req, ServletResponse resp,

 

      FilterChain chain) throws IOException, ServletException {

 

 

 

    HttpServletRequest request = (HttpServletRequest) req;

 

    HttpServletResponse response = (HttpServletResponse) resp;

 

 

 

    //检查提交数据是否包含禁用词,若含有则直接返回到浏览器,不存入数据库,不放行。

 

    Enumeration e = request.getParameterNames();

 

    while(e.hasMoreElements()){

 

      String name = (String) e.nextElement();

 

      String data = request.getParameter(name); 

 

      for(String regex : banWords){

 

        Pattern pattern = Pattern.compile(regex);

 

        Matcher m = pattern.matcher(data);

 

        if(m.find()){

 

          request.setAttribute("message", "文章中包括非法词汇,请检查后再提交!!");

 

          request.getRequestDispatcher("/message.jsp").forward(request, response);

 

          return;

 

        }

 

      }

 

    }

 

 

 

    //检查审核词 和 检查替换词 需要放行 以便存入到数据库中,只是在存入到数据库前,需要对数据进行敏感词处理(高亮或者替换)。

 

    chain.doFilter(new MyRequest(request), response); 

 

  }  

 

 

 

  class MyRequest extends HttpServletRequestWrapper{

 

    private HttpServletRequest request;

 

    public MyRequest(HttpServletRequest request) {

 

      super(request);

 

      this.request = request;

 

    }

 

    @Override

 

    public String getParameter(String name) {

 

 

 

      String data = this.request.getParameter(name);

 

      if(data==null){

 

        return null;

 

      }

 

      //检查审核词

 

      for(String regex : auditWords){

 

        Pattern p = Pattern.compile(regex);

 

        Matcher m = p.matcher(data);

 

        if(m.find()){    //我有一把仿真汽车,你要电鸡吗??

 

          String value = m.group();  //找出客户机提交的数据中和正则表达式相匹配的数据

 

          data = data.replaceAll(regex, "<font color='red'>" + value + "</font>");

 

        }

 

      }

 

 

 

      //检查替换词

 

      for(String regex : replaceWords){

 

        Pattern p = Pattern.compile(regex);

 

        Matcher m = p.matcher(data);

 

        if(m.find()){    //我有一把仿真汽车,你要电鸡吗??

 

          data = data.replaceAll(regex, "*******");

 

        }

 

      }

 

 

 

      return data;

 

    }

 

  }

 

 

 

  public void destroy() {

 

    // TODO Auto-generated method stub

 

  }

 

}

 

  

 

 

 

 web.xml文件

 

 

 

 

 


 

 

 

 

 

21、response对象的增强

 

        Servlet  API 中提供了response对象的Decorator设计模式的默认实现类HttpServletResponseWrapper , (HttpServletResponseWrapper类实现了response接口中的所有方法,但这些方法的内部实现都是仅仅调用了一下所包装的的 response对象的对应方法)以避免用户在对response对象进行增强时需要实现response接口中的所有方法。

 

 

 


 

 

 

22、response增强案例—压缩响应

 

应用HttpServletResponseWrapper对象,压缩响应正文内容。思路:

 

        (1) 通过filter向目标页面传递一个自定义的response对象。

 

                (a) 在自定义的response对象中,重写getOutputStream方法和getWriter方法,使目标资源调用此方法输出页面内容时,获得的是我们自定义的ServletOutputStream对象。

 

                (b) 在我们自定义的ServletOuputStream对象中,重写write方法,使写出的数据写出到一个buffer中。

 

        (2) 当页面完成输出后,在filter中就可得到页面写出的数据,从而我们可以调用GzipOuputStream对数据进行压缩后再写出给浏览器,以此完成响应正文件压缩功能。

 

 

 

Demo样例1:Filter类  

 

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);

 

    //备注:GZIPOutputStream 的压缩方式,只是对 文本文件(如:jsp、html、css、js) 压缩率很高,但是对于其他的二进制文件资源(如:视频、图片、音频)的压缩率非常一般,而且当二进制文件很大时(如视频文件很大),就会造成数据流 缓冲区的 溢出。所以该过滤器 应该仅 过滤压缩 文本文件,而避免过滤压缩二进制文件。

 

 

 

    response.setHeader("content-encoding", "gzip");

 

    response.setHeader("content-length", gzipout.length + "");

 

    response.getOutputStream().write(gzipout);

 

  }

 

 

 

  // 执行压缩功能的 GZIPOutputStream

 

  public byte[] gzip(byte b[]) throws IOException{    

 

    ByteArrayOutputStream bout = new ByteArrayOutputStream();

 

    GZIPOutputStream gout = new GZIPOutputStream(bout);

 

    gout.write(b);

 

    gout.close();

 

    return bout.toByteArray();

 

  }

 

 

 

  //采用Decorator装饰器的 自定义 Response类

 

  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();

 

    }

 

  }

 

 

 

  //自定义ServletOutputStream 类,用于返回给Servlet层调用

 

  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);

 

    }

 

 

 

  }

 

 

 

  public void destroy() {

 

    // TODO Auto-generated method stub

 

  }

 

 

 

  public void init(FilterConfig filterConfig) throws ServletException {

 

    // TODO Auto-generated method stub

 

  } 

 

}

 

 

 

Demo样例2:web.xml文件  

 

  <filter>

 

    <filter-name>GzipFilter</filter-name>

 

    <filter-class>cn.itcast.web.filter.example.GzipFilter</filter-class>

 

  </filter>

 

  <!--备注1:GZIPOutputStream 的压缩方式,只是对 文本文件(如:jsp、html、css、js) 压缩率很高,但是对于其他的二进制文件资源(如:视频、图片、音频)的压缩率非常一般,而且当二进制文件很大时(如视频文件很大),就会造成数据流 缓冲区的 溢出。所以该过滤器 应该仅 过滤压缩 文本文件,而避免过滤压缩二进制文件。-->

 

  <!--备注2: 我们很多时候 调用jsp文件都是通过转发的方式 调用(WEB-INF文件夹下面外界无法访问),所以一定要设定<dispatcher>FORWARD</dispatcher> -->

 

  <filter-mapping>

 

    <filter-name>GzipFilter</filter-name>

 

    <url-pattern>*.jsp</url-pattern>

 

    <dispatcher>FORWARD</dispatcher>

 

    <dispatcher>REQUEST</dispatcher>

 

  </filter-mapping> 

 

 

 

  <filter-mapping>

 

    <filter-name>GzipFilter</filter-name>

 

    <url-pattern>*.html</url-pattern>

 

  </filter-mapping> 

 

 

 

  <filter-mapping>

 

    <filter-name>GzipFilter</filter-name>

 

    <url-pattern>*.js</url-pattern>

 

  </filter-mapping>

 

 

 

Demo样例3:Servlet类  

 

 

 

 

 

Demo样例4:Service类 

 

 

 

 

 

 

 


 

 

 

23、实用案例-缓存数据到内存

 

对于页面中很少更新的数据,例如商品分类,为避免每次都要从数据库查询分类数据,因此可把分类数据缓存在内存或文件中,以此来减轻数据库压力,提高系统响应速度。

 

 

 

Demo样例1:Filter类   

 

public class WebCacheFilter implements Filter {

 

 

 

  private Map<String,byte[]> map = new HashMap();

 

 

 

  public void doFilter(ServletRequest req, ServletResponse resp,

 

      FilterChain chain) throws IOException, ServletException {

 

 

 

    HttpServletRequest request = (HttpServletRequest) req;

 

    HttpServletResponse response = (HttpServletResponse) resp;

 

 

 

    //1.得到用户想访问的资源(uri)

 

    String uri = request.getRequestURI();

 

 

 

    //2.看map集合中是否保存了该资源的数据

 

    byte b[] = map.get(uri);

 

 

 

    //3.如果保存了,则直接取数据打给浏览器

 

    if(b!=null){

 

      response.getOutputStream().write(b);

 

      return;

 

    }

 

 

 

    //4.如果没有保存数据,则放行让目标资源执行,这时还需写一个response的包装类,捕获目标资源的输出

 

    MyResponse my = new MyResponse(response);

 

    chain.doFilter(request, my);

 

    byte data[] = my.getBuffer();

 

 

 

    //5.以资源uri为关键字,打资源的数据保存map集合中,以备于下次访问

 

    map.put(uri, data);

 

 

 

    //6.输出数据给浏览器

 

    response.getOutputStream().write(data);

 

  }

 

 

 

  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);

 

    }    

 

  }

 

 

 

  public void init(FilterConfig filterConfig) throws ServletException {

 

    // TODO Auto-generated method stub

 

  }

 

 

 

  public void destroy() {

 

    // TODO Auto-generated method stub

 

  }

 

}

 

 

 

Demo样例2:web.xml文件  

 

 <filter>

 

    <filter-name>WebCacheFilter</filter-name>

 

    <filter-class>cn.itcast.web.filter.example.WebCacheFilter</filter-class>

 

  </filter>

 

 

 

  <filter-mapping>

 

    <filter-name>WebCacheFilter</filter-name>

 

    <url-pattern>/servlet/ServletDemo4</url-pattern>

 

  </filter-mapping> 

 

 

 

Demo样例3:Servlet类   

 

//显示分类的servlet

 

public class ServletDemo4 extends HttpServlet {

 

 

 

  public void doGet(HttpServletRequest request, HttpServletResponse response)

 

      throws ServletException, IOException {

 

 

 

    //查询数据库,获取到分类信息

 

    String data = "分类数据";    

 

 

 

    //输出分类信息

 

    response.getWriter().write(data);    

 

 

 

    System.out.println("hahahah");    

 

  }

 

 

 

  public void doPost(HttpServletRequest request, HttpServletResponse response)

 

      throws ServletException, IOException {

 

    doGet(request, response);

 

  }

 

}

 

 

 

 

 


 

 动态代理知识,未完待续……

 


 

 

敬请评论

(1)若您觉得本文 有用处  —— 请留言评论,以坚定其他 IT童鞋 阅读本文的信心。

(2)若您觉得本文 没用处  —— 请留言评论,笔者将会改进不足,以便为大家整理更加好用的笔记。
 

 


 

 

 

 

 

  • Servlet 知识详解(四)之  ——  Filter对象 过滤器  学习笔记
            
    
    博客分类: 学习笔记 Filter过滤器JavaServlet拦截请求 
  • 大小: 33.5 KB
  • Servlet 知识详解(四)之  ——  Filter对象 过滤器  学习笔记
            
    
    博客分类: 学习笔记 Filter过滤器JavaServlet拦截请求 
  • 大小: 41.4 KB
  • Servlet 知识详解(四)之  ——  Filter对象 过滤器  学习笔记
            
    
    博客分类: 学习笔记 Filter过滤器JavaServlet拦截请求 
  • 大小: 68.1 KB