j2ee中的过滤器Filter使用

一、过滤器入门

1. 概念:

    Filter 过滤器,又称拦截器

    实现 Filter 接口的类我们称之为 Filter (过滤器或拦截器)

    Filter能对用户访问的资源进行拦截

   

    在Filter里面可以只用 request获得请求消息  用response写入响应消息

    chain.doFilter(request, response) 方法放行  目标Servlet使用的是同一个请求和响应

    doFilter 方法后面的代码会执行,在目标Servlet 返回响应后执行, 也可以使用同一个请求和响应

 

2. 实现:

    1) 写一个类实现 Filter 接口 , 在doFilter 方法中写功能代码

    public class Filter1 implements Filter {

           public void doFilter(ServletRequest request, ServletResponse response,

           FilterChain chain) throws IOException, ServletException {

           System.out.println("before");

           chain.doFilter(request, response);

           System.out.println("after");

    }

    2) 在web.xml中配置Filter拦截的资源路径

    <filter>

       <filter-name>filter1</filter-name>

       <filter-class>cn.itcast.filter.Filter1</filter-class>

    </filter>

    <filter-mapping>

       <filter-name>filter1</filter-name>

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

    </filter-mapping>

 

3. Filter

    可以针对某一个url配置多个Filter, 这些Filter就会组成一个Filter链, 用FilterChain对象表示

    FilterChain对象的doFilter方法作用就是让Filter链上的当前拦截器放行,请求进入下一个Filter

 

    response 的中文编码问题, 只能在response.getWriter() 第一次被调用之前指定编码才有效

    一旦指定了编码,当前Filter链和目标Servlet使用的response都是同一个编码,因为用的本来就是一个

    response

 

4. Filter 与 Servlet

    Filter 就像一个特殊的Servlet

    Filter 在web容器启动是就初始化

    Filter 可以实现拦截功能,因为有个 FilterChain 对象,有个 doFilter方法可以实现对访问资源的放行

二、 Filter细节

1. Filter 的生命周期

    实现Filter的init和destroy方法就可以观察Filter的声明周期

    web容器启动时,会读web.xml文件,将所有的Filter都初始化

    Filter对象创建后会驻留在内存,当web应用移除或服务器停止时才销毁

 

Filter链中所有的Filter的拦截顺序   按照 在 web.xml 文件中的配置的先后顺序来进行拦截

 

2. 获得 Filter的初始化参数

    在 web.xml 文件中为Filter 配置初始化参数

    <init-param>

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

       <param-value>xxxx</param-value>

    </init-param>

    在 init 方法中读取配置文件

    public void init(FilterConfig filterConfig) throws ServletException {

       String name = filterConfig.getInitParameter("name");

      

    }

 

3. 拦截 html页面

1) 文件缓存

 由于html页面的url是没有在 web.xml 文件中配置的 服务器会调用DefaultServlet

在DefaultServlet 中会检查文件的修改时间, 如果没有修改则发送 304头

这样就会导致过滤器也被缓存

可以通过发送 200 状态码,但是 html 页面的数据仍然得不到读取

 

2) html 页面乱码

在 Filter 和 Html 中指定编码为 utf-8 , 这样会导致 html 页面中文乱码

原因是 html 页面数据会通过 DefaultServlet 发送

查看 web.xml 文件 发现DefaultServlet默认使用 gbk编码

修改配置 加初始化参数

<init-param>

      <param-name>fileEncoding</param-name>

      <param-value>utf-8</param-value>

 </init-param>

 

4. Filter拦截方式

Filter的dispatcher元素有4种取值, 分别代表四种拦截方式

REQUEST 拦截直接的请求方式

INCLUDE 拦截页面包含的访问方式

FORWARD 拦截请求转发访问方式

ERROR 拦截出错页面的访问方式

 

5. filter-mapping元素配置

拦截的url地址可以使用 /*  *.扩展名

<filter-mapping> 元素中可以配置多个地址 用于拦截多个url或servlet

对于多个条件都符合的url, filter会进行多次拦截

 

三、 过滤器案例

1. 缓存

禁止浏览器缓存所有动态页面

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

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

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

 

强制浏览器缓存所有的静态页面 html jpg css

String uri = request.getRequestURI();

String time = null;

if(uri.endsWith(".html"))

    time = config.getInitParameter("html");

else if(uri.endsWith(".jpg"))

    time = config.getInitParameter("jpg");

long l = Long.parseLong(time);

response.setDateHeader("Expires",System.currentTimeMillis() + l);

 

2. 实现用户自动登陆

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

2)编写过滤器检查用户是否带名为user的cookie来,如果有,检查用户名和密码做自动登陆

 

核心思路:

用户登陆后找LoginServlet , LoginServlet中做登陆,如果登陆成功, 获得用户选择的自动登陆时间

创建一个新的cookie 将用户名和密码用 “_”连接作为value,autoLogin作为name

设置cookie 的有效路径 request.getContextPath() 作用于整个web应用

设置cookie的有效时间为 autologintime

发送 cookie

 

写一个过滤器,对全站的资源进行拦截, 检查用户发送的cookie有没有一个名为autologin的

如果有 取出用户名和密码 再次做登陆处理 如果登陆成功, 将 user 存入session ,放行

 

出于安全性考虑, cookie 中的密码应该进行 md5 加密

 

3. 统一全站字符编码

response和request的post的方式好办

// 解决全站的乱码问题 request response

response.setContentType("text/html;charset=utf-8");

request.setCharacterEncoding("utf-8"); // 只对 post方式起作用,get方式不起作用

 

对于request的get方式需要手工转换,此时就需要用到 包装设计模式decorator

包装 getParameter方法 如果this.request.getMethod();是get就进行手动编码再返回value

if("get".equalsIgnoreCase(method))

       value = new String(value.getBytes("iso-8859-1"), "utf-8");

4. 为全站添加页眉和页脚

添加用户模块

 

5. 发送压缩后的响应数据

  简单分析:

    给 IE 浏览器会送的数据 需要进行gzip 压缩  访问速度快 省点浏览

    在最后将数据打给浏览器的时候, 将 response 中的数据全部压缩

    在过滤器放行的时候传入一个 假的 response 提供缓冲区, 这样后面的资源都会写入我的缓冲区

    缓冲区满了 或者 请求快结束的时候 将缓冲区的数据压缩后写入 真的 response  
   

    j2se 提供了一个流GZIPOutputStream 用于 gzip压缩

    j2se 提供了一个流 ZIPOutputStream 用于 zip 压缩

  具体思路:

   1当response传给servlet后servlet会调用getOutputStream往里写数据。

   2写入response的数据无法拿出 所以需要将response包装成我们可以拿到数据的myresponse再放行

   3.1在myresponse中传入被包装的response.

3.2然后重写getOutputStream和getWriter方法让这两个方法得到的对象的写出方法写出到一个指定的

缓冲区中

   3.3首先要建立指定的缓冲区ByteArrayOutputStream baout = new ByteArrayOutputStream();     

   3.4重写getOutputStream时发现ServletOutputStream对象不能指定缓冲区,

所以建立myOutputStream继承它,在构造时把缓冲区传给他的成员变量。

重写它的write方法,让它写入到传入的缓冲区。

public void write(int b) throws IOException {

       this.baout.write(b);// baout就是ByteArrayOutputStream

}

   3.5重写getWriter方法时发现PrintWriter可以new 并且需要传入一个输出字符流

正好可以将about转换成字符流发给他

new PrintWriter(new OutputStreamWriter(baout, "utf-8"));

但是没有必要每次调用getWriter时都要new一次,所以可以直接在myresponse构造方法中new

出来存入成员变量

   3.6再提供这成员变量ByteArrayOutputStream转换成数组后的get方法即可让调用者得到缓冲区

  byte[] bytes = this.baout.toByteArray();

       在get方法中需要关闭PrintWriter和baout流保证内容刷入缓冲区中

       为什么不关myOutputStream?因为在内部调用的是baout的write方法

   4.放行 将myresponse传入

   5.调用myresponse的getBuffer拿到缓冲区

   6.对缓冲区进行压缩

     ByteArrayOutputStream bout = new ByteArrayOutputStream();

GZIPOutputStream gout = new GZIPOutputStream(bout);

     gout.write(buffer);

     gout.close();

     byte[] bytes = bout.toByteArray();

   7.通知浏览器 内容是压缩的

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

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

     response.getOutputStream().write(bytes);

6. 跟踪用户上次访问时间

   1从cookies中拿到名为lastAccessTime的cookie,拿到值。

   2判断会话是否为新会话 因为如果是旧的就不给他打印时间了没必要每个页面都打印(具体需求)

     if(request.getSession().isNew()) 如果是第一次 lastAccessTime空 就输出欢迎你

     第二次就打印上次时间

   3将这次的时间放入cookie

   4将cookie设置入response并设置有效路径和有效时间

7. 统计站内各个页面的访问次数

  1在dofilter方法中 用request.getRequestURI获得请求的页面的uri

  2用request.getSession().getServletContext();拿到application域

  3以uri为key,计数器为value建map 将map存入application

  4放行

8. 实现某些资源只能被某些ip访问

   getRemoteAddr拿到访问者的ip 如果ip为特定IP 就打印一句话  否则放行

9. 实现防盗链功能

    针对所有的下载页面

   1同一下载url的命名或者

   2filter中request.getHeader("referer");拿到链接所在的网页地址

   3判断如果链接所在的不是webapp的子路径就直接跳转到首页

 

10.实现html标签转义、过滤敏感词汇 day23做的

12.强制缓存静态页面019/CacheFilter.java

   1判断一下访问的资源类型request.getRequestURI();

  

   2如果以.html .css .jpg等静态页面结尾 就读配置 看配置里要缓存多久 设置到time中

     如果不是 直接return

   3去掉禁止缓存消息头

response.setHeader("Cache-Control",null);

response.setHeader("Pragma",null);

   4.发缓存消息头

response.setDateHeader("Expires",System.currentTimeMillis() +Integer.parseInt(time)*60*60*1000);  

   5.放行

13.不让缓存非静态页面

   1直接发3个头 就不缓存了

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

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

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

2放行

14.cms自动生成静态页面day20/cms

   1一个jsp或者servlet访问多次会多次查数据库 为避免此现象 动态生成静态页面

   2在filter拿到请求,分析参数的id后猜测到将来生成html的web相对路径

   3根据web相对路径拿到这个html页面保存的绝对路径

   4根据绝对路径判断这个静态文件是否存在

   5如果静态文件不存在或者提交来的链接带刷新参数true则先建立这个文件目录 建立这个静态页面

   6再对response进行包装为myresponse 在包装类中 重写getWriter方法

因为getWriter的write写到的文件我们拿不到所以我们要自己new一个printWriter

让它写到指定的HTML的file,然后将这个printWriter返回,因为FlilWriter有乱码问题

所以用转换流

    pw = new PrintWriter(new OutputStreamWriter(new FileOutputStream(file), "utf-8"));

   7所以构造myresponse时传入的参数有静态页面文件和response。除了getWriter以外的别的方法交给

父类实现

   8如果静态页面存在或者提交来的参数为false则直接请求转发到静态页面(其实是请求转发到defaultservlet)

   应用场景:一个jsp或者servlet访问多次会多次查数据库 为避免此现象 动态生成静态页面

             如果jsp发生更改,通常是先跳转到一个更改servlet完成数据库的更新。那么在

             此servlet可以访问以下这个filter所拦截的servlet即可向这个filter传参。

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

URL url = new URL("http://localhost/day20_cms/servlet/ListServlet?id=" + id +"&flush=true");

            InputStream in = url.openStream();

            这句话完成了对被拦截页面的访问。     

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值