【java】Filter --- 过滤器

  • 1. 什么是Filter
  • 2. Filter的功能
  • 3. Filter的种类
  • 4. Filter的简单使用
  • 4.1 使用注解方式
  • 4.2 使用web.xml配置文件
  • 5. Filter的细节
  • 5.1 过滤器执行流程
  • 5.2 过滤器生命周期方法
  • 5.3 过滤器配置详解
  • 6. Filter链
  • 7. Filter案例1:登录验证
  • 8. Filter案例2:敏感词汇过滤(通过代理模式)


1. 什么是Filter

Filter是Java Web三大组件之一,它主要用于对用户请求(HttpServletRequest)进行预处理,也可以对服务器响应(HttpServletResponse)进行后处理,是个典型的处理链。

  • Filter与Servlet的区别在于:
    它不能直接向用户生成响应。
  • 完整的流程是:Filter对用户请求进行预处理,接着将请求交给Servlet进行处理并生成响应,最后Filter再对服务器响应进行后处理。

Filter在英文中是过滤器的意思,当然在此处的使用也是完美的切合了它的意思,我们使用filter的主要目的就是完成一个过滤的作用。可以在一个请求到达servlet之前,将其截取进行逻辑判断,然后决定是否放行到请求的servlet。也可以在一个response到达客户端之前,截取结果进行逻辑判断,然后决定是否允许返回给客户端。

最后需要注意的是,一个filter过滤器可以加在多个servlet控制器上,当然多个filter过滤器也是可以加在一个servlet控制器上的。由此也是可以看出来,我们使用filter往往是对一些公共的操作进行处理,例如:判断用户权限,解码本次请求等,还比如,我们的web应用中某些页面是需要用户登录后才能访问的,以往我们都是在每个servlet页面加上判断控制,导致代码冗余,有了filter,我们可以定义一个实现了filter的过滤器,让需要判断是否登录的页面都加上这么一个过滤器,可以大大降低代码的冗余程度。

2. Filter的功能

java过滤器记录登录日志_过滤器

在HttpServletRequest到达 Servlet 之前,拦截客户的HttpServletRequest 。根据需要检查HttpServletRequest,也可以修改HttpServletRequest 头和数据。

在HttpServletResponse到达客户端之前,拦截HttpServletResponse 。根据需要检查HttpServletResponse,也可以修改HttpServletResponse头和数据。

  • 应用场景
  1. 登录验证
  2. 统一设置编码格式
  3. 访问权限控制
  4. 敏感字符过滤等
  5. 等等…

3. Filter的种类

  • 用户授权的Filter:Filter负责检查用户请求,根据请求过滤用户非法请求。
  • 日志Filter:详细记录某些特殊的用户请求。
  • 负责解码的Filter:包括对非标准编码的请求解码。
  • Filter可拦截多个请求或响应;

4. Filter的简单使用

4.1 使用注解方式

package cn.siyi.web.filter;

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

@WebFilter("/*")//访问所有资源之前,都会执行该过滤器
public class FilterDemo1 implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("filterDemo1...");
        //放行
        filterChain.doFilter(servletRequest,servletResponse);
    }
    
    @Override
    public void destroy() {
    }
}

4.2 使用web.xml配置文件

package cn.siyi.web.filter;

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

public class FilterDemo1 implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("filterDemo1...");
        //放行
        filterChain.doFilter(servletRequest,servletResponse);
    }

    @Override
    public void destroy() {
    }
}
  • web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

    <filter>
        <filter-name>demo1</filter-name>//过滤器名称  
        <filter-class>cn.siyi.web.filter.FilterDemo1</filter-class>//过滤器类的包路径
        <!-- <init—param> //可选 -->
		    <!-- <param—name>参数名</param-name>//过滤器初始化参数 -->
		    <!-- <param-value>参数值</param-value> -->
		<!-- </init—pamm> -->
    </filter>
    <filter-mapping> //过滤器映射
        <filter-name>demo1</filter-name>
        <url-pattern>/*</url-pattern>//指定过滤器作用的对象
    </filter-mapping>
</web-app>

5. Filter的细节

5.1 过滤器执行流程

  1. 执行过滤器
  2. 执行放行后的资源
  3. 回来执行过滤器放行代码下边的代码

5.2 过滤器生命周期方法

init(FilterConfig filterConfig)
代表filter对象初始化方法,filter对象创建时执行

doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
代表filter执行过滤的核心方法,如果某资源在已经被配置到这个filter进行过滤的话,
那么每次访问这个资源都会执行doFilter方法

destory()代表是filter销毁方法,当filter对象销毁时执行该方法

5.3 过滤器配置详解

  • 拦截路径配置:
1. 具体资源路径: /index.jsp  只有访问index.jsp资源时,过滤器才会被执行
2. 拦截目录: 	/user/*		访问/user下的所有资源时,过滤器都会被执行
3. 后缀名拦截: 	*.jsp		访问所有后缀名为jsp资源时,过滤器都会被执行
4. 拦截所有资源:	/*			访问所有资源时,过滤器都会被执行
  • 拦截方式配置:资源被访问的方式
  • 注解配置:
* 设置dispatcherTypes属性
	1. REQUEST:默认值。浏览器直接请求资源
	2. FORWARD:转发访问资源
	3. INCLUDE:包含访问资源
	4. ERROR:错误跳转资源
	5. ASYNC:异步访问资源
  • web.xml配置
* 设置<dispatcher></dispatcher>标签即可//值和上面相同

6. Filter链

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

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

  • 执行顺序:如果有两个过滤器:过滤器1和过滤器2
  1. 过滤器1
  2. 过滤器2
  3. 资源执行
  4. 过滤器2
  5. 过滤器1
  • 过滤器先后顺序问题:
  1. 注解配置:按照类名的字符串比较规则比较,值小的先执行
  • 如: AFilter 和 BFilter,AFilter就先执行了。
  1. web.xml配置: 谁定义在上边,谁先执行

7. Filter案例1:登录验证

package cn.siyi.web.filter;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

/**
 * 登录验证的过滤器
 */
//@WebFilter("/*")
public class LoginFilter implements Filter {
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
        System.out.println(req);
        //0.强制转换
        HttpServletRequest request = (HttpServletRequest)req;
        //1.获取资源请求路径
        String uri = request.getRequestURI();
        //2.判断是否包含登录相关资源路径
        if(uri.contains("login.jsp") || uri.contains("/loginServlet") || uri.contains("/css/") || uri.contains("/js/") || uri.contains("/checkCodeServlet")||uri.contains("/fonts/")){
            //包含,用户就是想登录。放行
            chain.doFilter(req,resp);
        }else{
            //不包含,需要验证用户是否登录
            //3.从session中获取user
            Object user = request.getSession().getAttribute("user");
            if(user != null){
                //登录了,放行
                chain.doFilter(req,resp);
            }else{
                //没有登录.跳转登录页面
                request.setAttribute("login_msg","您尚未登录,请登录");
                request.getRequestDispatcher("/login.jsp").forward(req,resp);
            }
        }
    }

    public void init(FilterConfig config) throws ServletException {
    }

    public void destroy() {
    }
}

8. Filter案例2:敏感词汇过滤(通过代理模式)

package cn.siyi.web.filter;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.List;

@WebFilter("/*")
public class SensitiveWordsFilter implements Filter {

    private List<String> list = new ArrayList<>();//敏感词汇

    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
        //1.创建代理对象,增强getParameter方法
        ServletRequest proxy_req = (ServletRequest) Proxy.newProxyInstance(req.getClass().getClassLoader(), req.getClass().getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                //增强getParameter方法
                if(method.getName().equals("getParameter")){
                    //增强放返回值
                    //获取返回值
                    String value = (String) method.invoke(req, args);
                    if(value != null){
                        for (String str:list){
                            if(value.contains(str)){
                                value= value.replaceAll(str,"***");
                            }
                        }
                    }
                    return value;
                }
                return method.invoke(req,args);
            }
        });
        chain.doFilter(proxy_req, resp);
    }

    public void init(FilterConfig config) throws ServletException {
        try {
            //1.加载文件  获取文件真实路径
            ServletContext servletContext = config.getServletContext();
            String realPath = servletContext.getRealPath("/WEB-INF/classes/敏感词汇.txt");
            //2.读取文件
            BufferedReader bufferedReader = new BufferedReader(new FileReader(realPath));
            //3.将文件的每一行添加到list中
            String line=null;
            while((line=bufferedReader.readLine())!=null){
                list.add(line);
            }
            System.out.println(list);
            bufferedReader.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void destroy() {
    }
}