过滤器

过滤器

Java Web的三大组件

Servlet中组件一共有三种:Servlet过滤器监听器

组件作用实现接口
Servlet是一个运行在服务器端的Java小程序,用来接收请求并做出响应javax.servlet.Servlet
过滤器用于拦截用户的请求和响应,并且修改请求中的数据,对数据进行处理javax.servlet.Filter
监听器监听Web程序在运行过程中对作用域操作的事件,并且对事件进行处理 多个不同的接口javax.servlet.XxxListener

过滤器的概念

过滤器是服务器与客户端请求与响应的中间层组件,在实际项目开发中过滤器主要用于对浏览器的请求进行过滤处理,将过滤后的请求再转给下一个资源。与其他的WEB应用程序组件不同的是,过滤器是采用了“链”的方式进行处理的

这里写图片描述

过滤器的使用场景:
  1. 对用户登录权限进行拦截
  2. 实现一些日志记录的功能
  3. 集中处理处理一些公共的功能,如:汉字编码和解码

过滤器的执行特点:

与Servlet的执行不同,Servlet是有访问的地址。不是由用户主动调用,而是自动执行,是通过匹配用户的访问地址去进行过滤。

过滤器编写步骤:
  1. 开发过滤器的步骤:

    1. 编写一个类,实现javax.servlet.Filter接口
    2. 实现接口中所有的方法,其中doFilter()就是执行过滤任务
    3. 过滤器需要在web.xml中进行配置,配置与Servlet类似。
  2. 示例:创建一个过滤器HelloFilter,在运行HelloServlet前和后分别输出一句话,在HelloServlet中也输出一句话,观察控制台的运行效果。HelloServlet代表Web资源

image

/**
 * a)   编写一个类,实现javax.servlet.Filter接口
 * @author NewBoy
 *
 */
public class HelloFilter implements Filter {

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

    }

    /**
     * 执行过滤任务
     */
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        System.out.println("请求经过过滤器");
        //放行,让请求继续到达web资源(Servlet),调用chain中的方法
        chain.doFilter(request, response);
        System.out.println("响应回来经过过滤器");
    }

    @Override
    public void destroy() {

    }

}

Servlet

public void doGet(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
    System.out.println("我是HelloServlet,Web资源");
}

public void doPost(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
    doGet(request, response);
}
配置文件
<!-- 配置过滤器 -->
<filter>
    <!-- 过滤器的名字 -->
    <filter-name>hello</filter-name>
    <!-- 实现了Filter接口的类全名 -->
    <filter-class>com.filter.HelloFilter</filter-class>
</filter>
<filter-mapping>
    <!-- 过滤器的名字,与上面的一样 -->
    <filter-name>hello</filter-name>
    <!-- 要过滤的路径 -->
    <url-pattern>/*</url-pattern>
</filter-mapping>

过滤器的执行流程

image

  1. 用户发送请求,如果请求的地址匹配过滤器的url-pattern,则执行过滤器
  2. 执行过滤器中的doFilter方法,由chain.doFilter方法对请求进行放行
  3. 到达Web资源,响应回来的时候会再次经过过滤,执行响应的代码。

过滤器的生命周期:

  • 过滤器加载的时机:

    • Servlet是用户第1次访问的时候实例化,并且初始化。

    • 过滤器是在Web服务器启动加载当前项目完毕以后自动实例化

    image

生命周期的方法:

Filter接口中的方法
- void init(FilterConfig filterConfig)

- 在初始化过滤器的时候**执行1次**

- void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
- 参数:请求,响应,过滤链

- 请求和响应这两个接口是HttpServletRequest和HttpServletResponse的父接口

- 执行过滤的功能,**每次请求都会执行这个方法**

- public void destroy()
- 在服务器关闭的时候销毁,执行1次


过滤器的映射路径

Filter与Servlet中的区别:
  • 在Servlet中url-pattern是访问的地址

  • 在Filter中url-pattern是要过滤的地址

问:浏览器访问目标资源的路径,如果目标地址不存在,过滤器会不会运行?

如果Web资源不存在,只要匹配过滤的地址,同样会执行过滤器

在Filter中URL的过滤方式
匹配方式匹配哪些资源示例
完全匹配必须与访问地址精确匹配/demo1/aaa/bbb
目录匹配匹配某一个目录下所有的Web资源/aaa/*/*匹配所有的资源
扩展名匹配*.扩展名 匹配某一类扩展名*.do*.action
有关匹配的要点:
  • 以/开头的匹配模式和以扩展名结尾的配置方式,同时出现会怎样?

同时出现会在web项目加载的时候就失败,导致当前项目所有的web资源都无法访问。

image

过滤多个地址的写法:

一个filter-mapping中包含多个url-pattern
<filter>
    <filter-name>life</filter-name>
    <filter-class>com.filter.Demo1LifeCycleFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>life</filter-name>
    <url-pattern>/demo1</url-pattern>
    <url-pattern>/demo2</url-pattern>
</filter-mapping>
一个filter对应多个filter-mapping
<filter>
    <filter-name>life</filter-name>
    <filter-class>com.filter.Demo1LifeCycleFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>life</filter-name>
    <url-pattern>/demo1</url-pattern>
</filter-mapping>
<filter-mapping>
    <filter-name>life</filter-name>
    <url-pattern>/demo2</url-pattern>
</filter-mapping>
过滤Servlet的写法:
<filter>
    <filter-name>life</filter-name>
    <filter-class>com.filter.Demo1LifeCycleFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>life</filter-name>
    <!-- 指定Servlet的名字进行过滤 -->
    <servlet-name>HelloServlet</servlet-name>
</filter-mapping>

过滤器的拦截方式:

默认的拦截方式
  • 过滤器的拦截方式一共有4种
    REQUEST、FORWARD、INCLUDE、ERROR

  • 默认是请求的方式:只有直接来源于浏览器的请求,才经过过滤器。

来自转发的请求被拦截
  1. 在index.jsp转发到HelloServlet
<jsp:forward page="/demo1"></jsp:forward> 发现没有经过过滤器,但servlet还是访问了。
  1. 过滤器的配置
<filter>
    <filter-name>life</filter-name>
    <filter-class>com.filter.Demo1LifeCycleFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>life</filter-name>
    <url-pattern>/demo1</url-pattern>
    <!-- 配置拦截方式: 转发的时候经过过滤器 -->
    <dispatcher>FORWARD</dispatcher>
</filter-mapping>
  • 这样子转发也经过了过滤器
同时写多个拦截方式
<filter>
    <filter-name>life</filter-name>
    <filter-class>com.filter.Demo1LifeCycleFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>life</filter-name>
    <!-- 指定Servlet的名字进行过滤 -->
    <!-- <servlet-name>HelloServlet</servlet-name> -->
    <url-pattern>/demo1</url-pattern>
    <!-- 配置拦截方式: 转发的时候经过过滤器 -->
    <dispatcher>FORWARD</dispatcher>
    <!-- 拦截方式:浏览器直接的请求经过过滤器,默认的方式 -->
    <dispatcher>REQUEST</dispatcher>
</filter-mapping>
过滤器的拦截类型小结

过滤类型作用
REQUEST直接来源于浏览器的访问地址,(包含重定向)
FORWARD转发的时候经过过滤器
INCLUDE页面包络另一个页面访问时也经过过滤器
ERROR页面错误时经过过滤器

FilterConfig对象

  • 什么是FilterConfig对象:
    用于得到过滤器中的配置参数
<filter>
    <filter-name>Demo2ConfigFilter</filter-name>
    <filter-class>com.filter.Demo2ConfigFilter</filter-class>
    <!-- 过滤器的初始配置参数 -->
    <init-param>
        <param-name>charset</param-name>
        <param-value>GBK</param-value>
    </init-param>
    <init-param>
        <param-name>country</param-name>
        <param-value>China</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>Demo2ConfigFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>
FilterConfig三个方法:
方法名功能
String getInitParameter(String name)通过初始参数名得到参数值
Enumeration getInitParameterNames()得到所有的初始参数名字,返回枚举类
ServletContext getServletContext()得到上下文对象

- 在过滤器初始化init方法中把上面的配置参数输出

image

//得到初始的配置参数
String charset = filterConfig.getInitParameter("charset");
String country = filterConfig.getInitParameter("country");
System.out.println(charset);
System.out.println(country);
//得到所有的配置参数名
Enumeration<String> names = filterConfig.getInitParameterNames();
//遍历
while (names.hasMoreElements()) {
    //得到其中的一个名字
    String name = names.nextElement();
    System.out.println("名字:" + name + ", 值:" + filterConfig.getInitParameter(name));
}
案例:POST解码
  • 需求:编写过滤器,过滤所有Servlet中使用POST方法提交的汉字的编码。
    1. 有2个Servlet,一个是LoginServlet登录,一个是RegisterServlet注册
    2. 有2个JSP页面,1个是login.jsp,有表单,登录名。1个register.jsp,有表单,有注册的名字。都使用POST提交用户名使用汉字提交。
    3. 使用过滤器,对所有的Servlet的POST方法进行过滤
    4. 过滤的编码参数通过filterConfig得到
    5. 在没有使用过滤器之前,每个Servlet必须加上汉字编码:request.setCharacterEncoding(字符集); 字符集与网页的编码要一致

image

//过滤的编码参数,通过filterConfig得到
@Override
public void init(FilterConfig config) throws ServletException {
    charset = config.getInitParameter("charset");
    System.out.println(charset);
}

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
        throws IOException, ServletException {
    //设置汉字的编码, charset相当于utf-8
    request.setCharacterEncoding(charset);
    //放行
    chain.doFilter(request, response);
}

过滤器配置

<!-- 编码的过滤器 -->
<filter>
  <filter-name>EncodingFilter</filter-name>
  <filter-class>com.filter.EncodingFilter</filter-class>
  <!-- 编码的字符集 -->
  <init-param>
    <param-name>charset</param-name>
    <param-value>utf-8</param-value>
  </init-param>
</filter>
<filter-mapping>
  <filter-name>EncodingFilter</filter-name>
  <!-- 同时过滤2个Servlet -->
  <url-pattern>/login</url-pattern>
  <url-pattern>/register</url-pattern>
</filter-mapping>

过滤链

image

什么是过滤器链:

一次请求可以同时经过多个过滤器,每个过滤器会将请求传递给下一个过滤器,如果下一个没有过滤器了,则传递给Web资源,这多个过滤器就组成了一个过滤器链。请求的时候经过每一个过滤器,响应的时候以相反顺序再回到每一个过滤器。

过滤器的顺序是根据配置文件中配置的顺序

Filter接口:

Filter接口中的方法:过滤器接口
  • 生命周期的方法
Filter接口中的方法参数的作用
doFilter(ServletRequest request, ServletResponse response, FilterChain chain)**ServletRequest**:代表请求对象是HttpServletRequest接口的父接口,如果要使用HttpServletRequest对象,**需要对类型进行强转**。**ServletResponse**: 代表响应对象是HttpServletResponse的父接口,如果要使用HttpServletResponse对象,**需要对类型进行强转**。**FilterChain**:代表过滤链对象用于请求的放行。
FilterChain接口中的方法:过滤链接口

FilterChain接口中的方法 参数的作用
放行
doFilter(ServletRequest request, ServletResponse response) ServletRequest:代表请求对象,是HttpServletRequest接口的父接口,如果要使用HttpServletRequest对象,需要对类型进行强转。
ServletResponse: 代表响应对象,是HttpServletResponse的父接口,如果要使用HttpServletResponse对象,需要对类型进行强转。
将上一个请求传递给下一个过滤器或Web资源

示例:过滤器链
  • 需求:创建两个过滤器OneFilter和TwoFilter,访问ResourceServlet,每个过滤器的请求和响应各输出一句话,观察过滤器的执行过程。

image

第一个过滤器

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
        throws IOException, ServletException {
    //处理请求
    System.out.println("请求经过了第1个过滤器");
    //放行
    chain.doFilter(request, response);
    //处理响应
    System.out.println("响应经过了第1个过滤器");
}

第二个过滤器

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
        throws IOException, ServletException {
    //处理请求
    System.out.println("请求经过了第2个过滤器");
    //放行
    chain.doFilter(request, response);
    //处理响应
    System.out.println("响应经过了第2个过滤器");
}

*配置参数的顺序!!*

<!-- 配置第1个过滤器 -->
<filter>
    <filter-name>OneFilter</filter-name>
    <filter-class>com.filter.OneFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>OneFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

<!-- 配置第2个过滤器 -->
<filter>
    <filter-name>TwoFilter</filter-name>
    <filter-class>com.filter.TwoFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>TwoFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>
web.xml中出现的顺序,哪个配置在前面,哪个就是前面的过滤器。

案例

image

实现步骤:
1. 在WebRoot下创建页面 login.jsp上使用${msg},显示信息。
1. 创建LoginServlet, 判断用户名密码是否正确,如果正确,则在会话域中保存用户信息。登录成功跳转到list.jsp,登录失败则在域中写入登录失败的信息,并且跳转到login.jsp。

  1. 使用过滤器解决:创建LoginAuthorityFilter

    • 创建成员变量HashSet,用于保存所有不经过过滤器的页面

    • 在init方法中添加登录页面和登录的Servlet到HashSet中

    • 在doFilter过滤的方法中获取当前访问的路径,判断当前路径是否在集合中,如果在集合中则放行并返回
    • 否则判断会话域中是否有指定的用户信息,如果有则放行,并返回
    • 最后重定向到登录页面

image

过滤器
public class LoginAuthorityFilter implements Filter {

    //创建成员变量HashSet,用于保存所有不经过过滤器的页面
    private HashSet<String> paths = new HashSet<String>();

    @Override
    public void destroy() {
    }

    @Override
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
            throws IOException, ServletException {
        //得到子接口
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) resp;
        //得到会话域
        HttpSession session = request.getSession();
        //在doFilter过滤的方法中获取当前访问的路径,判断当前路径是否在集合中
        String path = request.getServletPath();
        System.out.println(path);
        if (paths.contains(path)) {
            //如果在集合中则放行并返回
            chain.doFilter(request, response);
            return;
        }
        //否则判断会话域中是否有指定的用户信息
        String user = (String) session.getAttribute("user");
        if (user != null) {
            //如果有则放行,并返回
            chain.doFilter(request, response);
            return;
        }
        System.out.println("拦截到非法的用户:" + request.getRemoteAddr());
        //最后重定向到登录页面
        response.sendRedirect(request.getContextPath() + "/login.jsp");
    }

    @Override
    public void init(FilterConfig config) throws ServletException {
        // 在init方法中添加登录页面和登录的Servlet到HashSet中
        paths.add("/login.jsp");
        paths.add("/login");
    }

}

servlet
HttpSession session = request.getSession();
//得到用户名和密码
String name = request.getParameter("user");
String pwd = request.getParameter("pwd");
//判断是否登录成功
if ("newboy".equals(name) && "123".equals(pwd)) {
    //如果登录成功,将用户的信息保存到会话域中
    session.setAttribute("user", name);
    //跳转到list.jsp
    response.sendRedirect(request.getContextPath() + "/list.jsp");
}
//如果登录失败,写错误信息到请求域,跳转到login.jsp显示出来
else {
    request.setAttribute("msg", "用户名或密码不正确");
    request.getRequestDispatcher("/login.jsp").forward(request, response);
}
  • 18
    点赞
  • 68
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值