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

javaWeb之过滤器(Filter)

程序员文章站 2022-07-15 10:44:50
...

Servlet技术规范 描述三种技术 : Servlet(服务器小程序) 、Filter(过滤器)、 Listener(监听器)

1 新建类 继承httpServlet
2 重写doGet 和 dopost方法
3 在web.xml中 注册和映射

1. Filter简介

Filter也称之为过滤器,它是Servlet技术中最实用的技术,
例如Jsp, Servlet, 静态图片文件或静态 html 文件等进行拦截,
从而实现一些特殊的功能。例如实现URL级别的权限访问控制、
过滤敏感词汇、解决get和post乱码等一些高级功能。
Servlet API中提供了一个Filter接口,开发web应用时,如果编写的
Java类实现了这个接口,则把这个java类称之为过滤器Filter。
通过Filter技术,开发人员可以实现用户在访问某个目标资源之前,
对访问的请求和响应进行拦截,如下所示:

javaWeb之过滤器(Filter)

2.Filter是如何实现拦截的?
Filter接口中有一个doFilter方法,当开发人员编写好Filter,并配置对哪个web资源(拦截url)进行拦截后,WEB服务器每次在调用web资源之前,都会先调用一下filter的doFilter方法,因此,在该方法内编写代码可达到如下目的:
调用目标资源之前,让一段代码执行
是否调用目标资源(即是否让用户访问web资源)。
web服务器在调用doFilter方法时,会传递一个filterChain对象进来,filterChain对象是filter接口中最重要的一个对象,它也提供了一个doFilter方法,开发人员可以根据需求决定是否调用此方法,调用该方法,则web服务器就会调用web资源的service方法,即web资源就会被访问,否则web资源不会被访问。
调用目标资源之后,让一段代码执行

javaWeb之过滤器(Filter)

Filter运行在服务器端,对服务器端web资源的访问 进行拦截,起到过滤的作用

Servlet API中 定义接口 Filter,用户只需要编写程序实现Filter接口,完成过滤器编写

  1. Filter快速入门
    3.1. 准备工作
    hello.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
    <h1>Hello,JSP</h1>
</body>
</html>
// HelloServlet
public class HelloServlet extends HttpServlet {
    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        response.getWriter().println("<h1>Hello,Servlet</h1>");
    }

    public void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        doGet(request, response);
    }
}

3.2. 编写类 实现 Filter接口

/**
 * 编写为过滤器,过滤器作用 用来拦截web资源的访问
 * 
 * @author seawind
 * 
 */
public class Filter1 implements Filter {

    @Override
    public void destroy() {
    }
    @Override
    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException {
        // 执行过滤
        System.out.println("执行Filter1 ");

        // 拦截后,如何让目标资源执行
        chain.doFilter(request, response);
    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }
}

3.3. 在服务器端注Filter(配置拦截哪个web资源—– web.xml

 <!-- 注册过滤器 -->
  <filter>
    <filter-name>Filter1</filter-name>
    <filter-class>cn.itcast.filter.Filter1</filter-class>
  </filter> 

  <!-- 配置过滤器去拦截哪个资源 -->
  <filter-mapping>
    <filter-name>Filter1</filter-name>
    <url-pattern>/hello.jsp</url-pattern>
    <!-- 拦截/hello是Servlet 路径 -->
    <!--    <url-pattern>/hello</url-pattern>-->
    <!-- 拦截Servlet 还可以通过Servlet 名称进行拦截 -->
    <servlet-name>HelloServlet</servlet-name>
  </filter-mapping>

3.4. 客户端访问被拦截目标资源之前,服务器调用Filter的doFilter方法 ,执行过滤

3.5. Filter的doFilter方法中传入 FilterChain, 如果调用FilterChain的doFilter 就会执行目标资源,否则目标资源不会执行

javaWeb之过滤器(Filter)

问题1: 每次使用过滤器都需要 实现filter接口, 再去web.xml中配置, 是否很麻烦? 有没有更加简便的方法?
问题2: 如果配置两个过滤器谁先执行,谁后执行? 由什么决定的?
4. FilterChain

多个过滤器组成过滤器链,谁先执行,谁后执行?

在客户端访问服务器web资源时,服务器端为一个web资源,配置多个过滤器拦截 ,这多个过滤器,就会组成过滤器链 FilterChain, 调用FilterChain的doFilter 表示要执行过滤器链下一个资源,如果当前过滤器已经是链上最后一个过滤器,就会执行目标资源

  • web服务器根据Filter在web.xml文件中的注册顺序,决定先调用哪个Filter

    1. Filter生命周期
      我相信在北京大家都有乘坐地铁的经历, 为了安全期间, 我们必须安检部门的检查, 通过后才可以乘坐地铁.
      1 乘坐地铁服务 对应 服务器上资源服务
      2 安检部门 对应 过滤器

安检部门上岗前, 必须 发放安检设备
每次安检,都有严格的流程
结束安检, 需要回收安检设备

public class AnjianFilter implements Filter {
    public AnjianFilter() {
        System.out.println("构造方法: 地铁安检部门成立----------------------");
    }
    public void init(FilterConfig fConfig) throws ServletException {
        System.out.println("init: 安检人员上岗前的准备工作----------------------");
    }

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        System.out.println("doFilter: 每次有人进入地铁必须进行安全检查----------------------" + new Date().toLocaleString());
        // 允许通过
        chain.doFilter(request, response);
    }

    public void destroy() {
        System.out.println("destroy: 收回安检设备----------------------");
    }
}

init(FilterConfig) doFilter(request,response,filterChain) destroy()
1、Filter对象在tomcat服务器启动时 创建,调用init方法 (只会创建一个对象,init方法执行一次)
2、doFilter 每次拦截目标资源时,执行
3、destroy 服务器关闭时执行

请思考:
安检部门 如果在非节假日客流量少时,5个手持安检设备就够了.
但是碰上节假日客流量多时,10个手持安检设备才够用,需要向上级申请.
节假日过后客流量少了,4个手持安检设备就够了,还需要再次向上级申请换回去…
如此频繁的申请,等待通过是否很麻烦,大家有没有好的主意?
6. FilterConfig对象
FilterConfig 作用和 ServletConfig 类似,用来在Filter初始化阶段,将参数传递给过滤器
1、通过 String getInitParameter(String name) 获得过滤器初始化参数
* FilterConfig 提供参数,是Filter类私有参数,Filter2的初始化参数,不能在Filter1 中进行获取
* 配置全局参数, 进行配置,通过ServletContext 获得
javaWeb之过滤器(Filter)
javaWeb之过滤器(Filter)
7. 过滤器拦截配置

javaWeb之过滤器(Filter)

javaWeb之过滤器(Filter)

1、如果连接目标资源是一个Servlet,可以选择url和servlet名称两种配置方式

<!-- 拦截/hello是Servlet 路径 -->
<url-pattern>/hello</url-pattern>
<!-- 拦截Servlet 还可以通过Servlet 名称进行拦截 -->
<servlet-name>HelloServlet</servlet-name>

2、url-pattern 和 Servlet中路径写法一样,有三种 : 完全匹配、目录匹配、扩展名匹配
3、指定过滤器所拦截的资源被 Servlet 容器调用的方式
容器调用服务器端资源 有四种方式
REQUEST、FORWARD、INCLUDE、ERROR

  1. Filter应用
    8.1. 案例1:解决post方式获取参数中文乱码的编码过滤器
    案例:编写jsp 输入用户名,在Servlet中获取用户名,将用户名输出到浏览器上
// 处理请求post乱码代码
request.setCharacterEncoding("utf-8");
// 设置响应编码集代码`这里写代码片`
response.setContentType("text/html;charset=utf-8");

经常会使用,而过滤器可以在目标资源之前执行,将很多程序中处理乱码公共代码,提取到过滤器中 ,以后程序中不需要处理编码问题

需求: 要求用户输入用户名,显示在页面上

8.1.1. input.jsp

<form action="${pageContext.request.contextPath }/demo1" method="post">
    请输入你的大名<input type="text" name="username"/><br/>
    <input type="submit" value="提交"/>
</form>

8.1.2. Demo1Serlvet

public class Demo1Servlet extends HttpServlet {
    private static final long serialVersionUID = 1L;

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

    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String username = request.getParameter("username");
        response.getWriter().println("幸会," + username);
    }
}

8.1.3. EncodingFilter

public class EncodingFilter implements Filter {
    public EncodingFilter() {
    }

    public void destroy() {
    }

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        // 解决获取参数出现中文乱码
        request.setCharacterEncoding("utf-8");
        // 解决浏览器出现中文乱码
        response.setContentType("text/html;charset=utf-8");
        // 放行
        chain.doFilter(request, response);
    }

    public void init(FilterConfig fConfig) throws ServletException {
    }
}

案例1缺点: 可以解决post方式的乱码, 但是解决get方式的乱码吗? 如果不可以请思考怎么做?
案例2:解决整个项目的乱码

<form action="/day16/demo2Servlet" method="post">
    昵称<input type="text" name="nickname"/><br/>
    城市<input type="text" name="city"/><br/>
    <input type="submit" value="提交"/>
</form>
public class GenericEncodingFilter implements Filter {
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        // 为了能够使用子类的方法,需要向下转型
        HttpServletRequest httpServletRequest = (HttpServletRequest)request;
        // 使用包装 对方法进行增强
        HttpServletRequest myRequest = new MyRequest(httpServletRequest);
        // 放行
        chain.doFilter(myRequest, response);
    }

    public void init(FilterConfig fConfig) throws ServletException {
    }

    @Override
    public void destroy() {

    }

    class MyRequest extends HttpServletRequestWrapper{
        private HttpServletRequest httpServletRequest;

        // 记录是否修改过
        private boolean isUpdate = false;

        public MyRequest(HttpServletRequest request) {
            super(request);
            this.httpServletRequest = request;
        }

        @Override
        public Map<String, String[]> getParameterMap() {
            // 1 获取请求方式
            String method = httpServletRequest.getMethod();
            // 2 根据不同方式,做出对应的处理
            if("post".equalsIgnoreCase(method)) {
                // 设置请求编码集
                try {
                    httpServletRequest.setCharacterEncoding("utf-8");
                    return httpServletRequest.getParameterMap();
                } catch (UnsupportedEncodingException e) {
                    e.printStackTrace();
                }
            }else if("get".equalsIgnoreCase(method)) {
                // 先编码,再解码
                Map<String, String[]> parameterMap = httpServletRequest.getParameterMap();
                if(!isUpdate) {
                    try {
                        for(Entry<String, String[]> entry : parameterMap.entrySet()){
                            String[] valueArr = entry.getValue();
                            for(int i=0; i<valueArr.length; i++) {
                                valueArr[i] = new String(valueArr[i].getBytes("iso-8859-1"), "utf-8");
                            }
                        }
                    } catch (UnsupportedEncodingException e) {
                        e.printStackTrace();
                    }
                    isUpdate = true;
                }
                return parameterMap;
            }
            return super.getParameterMap();
        }

        @Override
        public String getParameter(String name) {
            Map<String, String[]> parameterMap = this.getParameterMap();
            String[] valueArr = parameterMap.get(name);
            if(valueArr!=null) {
                return valueArr[0];
            }else {
                return null;
            }
        }
        @Override
        public String[] getParameterValues(String name) {
            return this.getParameterMap().get(name);
        }
    }
}

==================================================================================================================
过滤器
1、过滤器编写步骤
2、全局编码 过滤器
3、通用get/post乱码过滤器 ———— 理解实现过程 ,保存起来会使用

相关标签: Filter