如何学习过滤器(Filter)——入门

概念

过滤器是Servlet的高级特性之一。但是也不必想的那么高深,归根结底也只是一个用来实现的Java类

我们来看看web容器中对数据的处理是怎么处理的:
在这里插入图片描述
从上面的图我们可以发现,当浏览器发送请求给服务器的时候,先执行过滤器,然后才访问Web的资源。服务器响应Response,从Web资源抵达浏览器之前,也会途径过滤器。。

我们很容易发现,过滤器可以比喻成一张滤网。我们想想现实中的滤网可以做什么:在泡茶的时候,过滤掉茶叶。那滤网是怎么过滤茶叶的呢?规定大小的网孔,只要网孔比茶叶小,就可以实现过滤了!

引申在Web容器中,过滤器可以做:过滤一些敏感的字符串【规定不能出现敏感字符串】、避免中文乱码【规定Web资源都使用UTF-8编码】、权限验证【规定只有带Session或Cookie的浏览器,才能访问web资源】等等等,过滤器的作用非常大,只要发挥想象就可以有意想不到的效果

也就是说:当需要限制用户访问某些资源时、在处理请求时提前处理某些资源、服务器响应的内容对其进行处理再返回、我们就是用过滤器来完成的!

为什么要使用过滤器

举个简单的例子:用过滤器对数据进行编码的测试

没有使用过滤器的数据编码

为了方便,我们还是创建一个Maven项目,名字呢自己随便取,心里有数就好。然后写好简单得表单页面和Servlet处理页面;我这儿就直接贴截图了
在这里插入图片描述
在这里插入图片描述
其中index.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
  <head>
    <title>用户表单</title>
  </head>
  <body>
  <h3>用户登录</h3>
  <form action="filter" method="post">
    <table>
      <tr>
        <td>用户名:</td>
        <td><input type="text" name="username"></td>
      </tr>
      <tr>
        <td colspan="2">
          <input type="submit" value="提交">
          <input type="reset" value="重置">
        </td>
      </tr>
    </table>
  </form>
  </body>
</html>

FilterServlet.java

import java.io.IOException;

public class FilterServlet extends javax.servlet.http.HttpServlet {
    protected void doPost(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException {

        String username=request.getParameter("username");
        System.out.println(username);
    }

    protected void doGet(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException {

    }
}

代码很简单,就十几行左右。我们打开浏览器看看效果
在这里插入图片描述
然后我们再去控制台看看
在这里插入图片描述
嗯哼。。。乱码了。
也就是说,如果一个网页中有多个表单验证,我们就要写很多行编码的代码。这样的话代码重复率太高了。而且代码的耦合度也低。那么这时候使用过滤器就会去过滤一遍我们的数据,然后对我们的数据进行编码。

我们这时候再看看,使用过滤器之后的数据格式

使用过滤器之后的数据编码

假如我们在过滤器中直接指定数据的编码格式,那么无论任何数据在到达服务器端的时候都会先走一遍过滤器,然后才能到达服务器。

过滤器API

只要Java类实现了Filter接口,那么这个类就可以当作一个过滤器。

我们来看看他的源码:
在这里插入图片描述

其中的init()方法和destroy()方法不需要我们去过多的关注了。这就是他的启动与销毁方法。我们需要关注的是doFilter()方法。
值得注意的是doFilter()方法,它有三个参数(ServletRequest,ServletResponse,FilterChain),从前两个参数我们可以发现:过滤器可以完成任何协议的过滤操作!

那FilterChain是什么东西呢?我们看看:
在这里插入图片描述

咦。。。怎么又是两个request和response参数。。这啥意思啊。

我们就可以这样去理解。里面的那个FilterChain相当于老二过滤器,外层的过滤器相当于老大过滤器。
举个例子:大家应该见过网上的那个净水器吧。一共有好多好多层。我们就可以理解为很多很多层过滤器。

好了,我们基础知识理解了,我们还是看看怎么去写一个过滤器吧。

快速入门
一个简单的过滤器

上面我们说过,只要实现了Filter接口的都算是过滤器。那么我们就可以这样写;

import javax.servlet.*;
import java.io.IOException;

public class FilterServlet extends javax.servlet.http.HttpServlet implements Filter {
    protected void doPost(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException {

        String username=request.getParameter("username");
        System.out.println(username);
    }

    protected void doGet(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException {

    }

    public void init(FilterConfig filterConfig) throws ServletException {

    }

    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        
    }
}

在这里插入图片描述
然后在我们的doFilter方法体里边写处理代码:

 public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {

        filterChain.doFilter(servletRequest, servletResponse);
    }

当然方法体写好了之后,同样Filter也是需要部署在服务器上的

filter部署

打开我们的web.xml。然后在下面添加如下配置:

    <filter>
        <filter-name>FilterServlet</filter-name>
        <filter-class>net.zxy.servlet.FilterServlet</filter-class>
        <init-param>
            <param-name>word_file</param-name>
            <param-value>/WEB-INF/word.txt</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>FilterServlet</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
  • <filter-name>:用于为过滤器指定一个名字,并且该字段不能为空

  • filter-class:用于指定过滤器的实现类

  • init-parm:用于为过滤器指定初始化参数,他的子元素是这个初始化参数的值

  • filter-name:指定参数的值。在过滤器中,可以使用FilterConfig接口对象来访问初始化参数。

  • filter-mappping:用于设置一个Filter拦截的资源

  • filter-name:必须和上面的filter-name对应相同

  • url-pattern:设置 filter 所拦截的请求路径(过滤器关联的URL样式)。/*代表全部拦截

  • <servlet-name>:指定过滤器所拦截的Servlet名称。

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

  • <dispatcher>: 子元素可以设置的值及其意义:

    • REQUEST:当用户直接访问页面时,Web容器将会调用过滤器。如果目标资源是通过RequestDispatcher的include()或forward()方法访问时,那么该过滤器就不会被调用。
    • INCLUDE:如果目标资源是通过RequestDispatcher的include()方法访问时,那么该过滤器将被调用。除此之外,该过滤器不会被调用。
    • FORWARD:如果目标资源是通过RequestDispatcher的forward()方法访问时,那么该过滤器将被调用,除此之外,该过滤器不会被调用。
    • ERROR:如果目标资源是通过声明式异常处理机制调用时,那么该过滤器将被调用。除此之外,过滤器不会被调用。
过滤器的执行顺序

上面已经说过了,过滤器的doFilter()方法是极其重要的,FilterChain接口是代表着所有的Filter,FilterChain中的doFilter()方法决定着是否放行下一个过滤器执行(如果没有过滤器了,就执行目标资源)。

测试1
试试在doFilter()方法中输出一句话然后再调用过滤器:

public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {

        System.out.println("我是过滤器1");
        filterChain.doFilter(servletRequest, servletResponse);
    }

在这里插入图片描述
访问页面,表单提交之后发现确实被执行了。而且还执行了三次(想一想为什么会被执行3次呢)。

测试2
我们再把doFilter()方法注释掉之后看看;

public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {

        System.out.println("我是过滤器1");
//        filterChain.doFilter(servletRequest, servletResponse);
    }

在这里插入图片描述
我们看到,并没有任何输出,是因为我们打开服务器之后,主页面都没有任何内容;
在这里插入图片描述
测试3
我们在过滤器执行之后再看看,再试着打印出一段话看看会不会被执行

public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {

        System.out.println("我是过滤器1");
        filterChain.doFilter(servletRequest, servletResponse);
        System.out.println("过滤器1执行完成,开始放行");
    }

在这里插入图片描述
好像也没啥问题

也就是说,我们在使用了过滤器之后,会自动的去过滤掉我们规定的信息。

Filter的简单应用

filter有三种典型的应用

  • 可以在filter中根据条件选择是否执行filterChain.doFilter()方法,即是否让资源放行
  • 在让目标资源执行之前,可以对request,response作预处理,再让目标资源执行
  • 在目标资源执行之后,可以捕获目标资源的执行结果,从而实现一些特殊的功能

我们来做个小东西
禁止让浏览器缓存动态页面

我们先看看当前的响应头是怎么样的:
在这里插入图片描述
然后我们去设置他的响应头,再执行过滤器看看效果是什么样的

  public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        /*
        * 让web资源不缓存,直接设置http中的response就行
        * 由于我们使用的是http协议,ServletResponse中没有设置请求头的方法,所以我们要强制转换一下
        * 直接转换成http类型的,,让他知道知道社会的黑暗
        * */
        HttpServletRequest request= (HttpServletRequest) servletRequest;
        HttpServletResponse response= (HttpServletResponse) servletResponse;

        response.setDateHeader("Expires",-1);
        response.setHeader("Cache-Control","np-cache");
        response.setHeader("Pragma","no-cache");

        // 上面的设置就是将目标资源设置成不缓存的了
        filterChain.doFilter(servletRequest, servletResponse);
    }

我们再看看他的响应头:
在这里插入图片描述

最后我们来使用过滤器来做一个简单得小Demo,实现自动登录;

案例——使用过滤器实现自动登录

先给大家看一眼工程结构:
在这里插入图片描述
主体类User

public class User {
    private String username,password;

    public User() {
    }

    public User(String username, String password) {
        this.username = username;
        this.password = password;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}

实验数据库类UserDB

import net.zxy.bean.User;

import java.util.ArrayList;
import java.util.List;

public class UserDB {
    private static List<User> users=new ArrayList<User>();

    static {
        users.add(new User("aaa","123"));
        users.add(new User("bbb","213"));
        users.add(new User("ccc","321"));
    }
    public static List<User> getUsers(){
        return users;
    }

    public static void setUsers(List<User> users) {
        UserDB.users = users;
    }
}

Dao层UserDao


import net.zxy.bean.User;
import net.zxy.bean.beanDB.UserDB;

import java.util.List;

public class UserDao {
    public static User find(String username,String password){
        List<User> userList= UserDB.getUsers();

        // 遍历集合
        for (User user:userList){
            if (user.getUsername().equals(username) && user.getPassword().equals(password)){
                return user;
            }
        }
        return null;
    }
}

LoginServlet

package net.zxy.servlet;

import net.zxy.bean.User;
import net.zxy.dao.UserDao;

import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;
import java.io.IOException;

@WebServlet(name = "LoginServlet",value = "/login")
public class LoginServlet extends HttpServlet implements Filter {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

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

        UserDao userDao=new UserDao();
        User user=userDao.find(username,password);
        if (user==null){
            request.setAttribute("errMsg","用户名或密码错误");
            response.sendRedirect("login.jsp");
        }

        HttpSession session=request.getSession();
        session.setAttribute("errMsg",user);
        request.setAttribute("errMsg","你已登陆成功");

        // 如果用户关闭了浏览器,那么这时候就需要Cookie了
        Cookie cookie=new Cookie("autoLogin",user.getUsername()+"."+user.getPassword());

        // 设置Cookie的生命周期
        cookie.setMaxAge(Integer.parseInt(request.getParameter("time"))*60);
        // 把Cookie返回给浏览器
        response.addCookie(cookie);

    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }

    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletResponse response=(HttpServletResponse) servletResponse;
        HttpServletRequest request=(HttpServletRequest) servletRequest;

        // 如果用户没有关闭浏览器,那么就不需要Cookie
        if (request.getSession().getAttribute("user")!=null){
            filterChain.doFilter(request,response);
            return;
        }

        // 如果用户关闭了浏览器,那么就需要Cookie中的信息来实现登录
        Cookie[] cookies=request.getCookies();

        String value=null;
        for (int i=0;cookies!=null && i<cookies.length;i++){
            if (cookies[i].getName().equals("autoLogin")){
                value=cookies[i].getValue();
            }
        }
        // 得到Cookie中的用户名和密码
        if (value!=null){
            String username=value.split("\\.")[0];
            String password=value.split("\\.")[1];

            UserDao userDao=new UserDao();
            User user=userDao.find(username,password);

            if (user!=null){
                request.getSession().setAttribute("user",user);
            }
        }
        filterChain.doFilter(request,response);
    }
}

login.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>用户登录</title>
</head>
<body>
<h3>用户登录</h3>
<form action="login" method="post">
    用户名:<input type="text" name="username"><br>
    密码:<input type="password" name="password"><br>

    <input type="radio" name="time" value="10">10分钟
    <input type="radio" name="time" value="30">30分钟
    <input type="radio" name="time" value="60">60分钟
    <br>

    <input type="submit" value="登录">
</form>
</body>
</html>

展示结果自己去试试吧。。我没找到电脑的录屏工具。艹,不知道哪儿去了

改进

我们在使用Cookie的时候会发现,我们的密码都是明文加上去。万一有个懂编程的瞅一眼,那不就是白搞了嘛,所以哦我们还需要对我们的密码进行加密处理一下。。具体代码就不写了,你们自己去搞定吧。嘿嘿嘿,就是这么任性。

小结

在JavaWeb开发中,为了避免代码的重复性使用和表单数据的验证,我们通常会使用Filter(过滤器)对数据进行过滤一遍再发送到服务器,从而达到低耦合效果。

谢谢观看,本文参考自Java3y的博客,如有侵犯,请联系我删除。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

鱼小洲

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值