Filter 介绍

Filter 可认为是 Servlet 的一种加强版,它主要用于对用户请求进行预处理,也可以对 HttpServletResponse 进行后处理,是个典型的处理链。

Filter 也可对用户请求生成响应,这一点与 Servlet 相同,但实际上很少会使用 Filter 向用户请求生成响应。使用 Filter 完整的流程是:Filter 对用户请求进行预处理,接着将请求交给 Servlet 进行处理并生成响应,最后 Filter 再对服务器响应进行后处理。

 

Filter 有如下几个用处:

1、在 HttpServletRequest 到达 Servlet 之前,拦截用户的 HttpServletRequest

2、根据需要检查 HttpServletRequest,也可以修改 HttpServletRequest 头和数据

3、在 HttpServletResponse 到达客户端之前,拦截 HttpServletResponse

4、根据需要检查 HttpServletResponse,也可以修改 HttpServletResponse 头和数据

 

Filter 主要有如下几个种类:

1、用户授权的 Filter:Filter 负责检查用户请求,根据请求过滤用户非法请求。

2、日志 Filter:详细记录某些特殊的用户请求。

3、负责解码的 Filter:包括对非标准编码的请求解码。

4、能改变 XML 内容的 XSLT Filter 等。

5、Filter 可负责拦截多个请求或响应:一个请求或响应也可被多个 Filter 拦截。

 

创建一个 Filter 只需两个步骤:

1、创建 Filter 处理类

2、web.xml 中配置 Filter

 

创建 Filter 类

创建 Filter 必须实现 javax.servlet.Filter 接口,在该接口中定义了如下三个方法:

1、void init(FilterConfig config):用于完成 Filter 的初始化

2、void destroy():用于 Filter 销毁前,完成某些资源的回收

3、void doFilter(ServletRequest request, ServletResponse response, FilterChain chain):实现过滤功能,该方法就是对每个请求及响应增加的额外处理。

 

LogFilter.java

package com.baiguiren;

import java.io.IOException;

import javax.servlet.*;

@WebFilter(filterName="log", urlPatterns={"/*"})
public class LogFilter implements Filter
{
    // FilterConfig 可用于访问 Filter 的配置信息
    private FilterConfig config;

    // 实现初始化方法
    public void init(FilterConfig config)
    {
        this.config = config;
    }

    // 实现销毁方法
    public void destroy()
    {
        this.config = null;
    }

    // 执行过滤的核心方法
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
        throws IOException, ServletException
    {
        // 下面代码用于对用户请求进行预处理
        // 获取 ServletContext 对象,用于记录日志
        ServletContext context = this.config.getServletContext();
        long before = System.currentTimeMillis();
        System.out.println("开始过滤...");
        // 将请求转换成 HttpServletRequest 请求
        HttpServletRequest hRequest = (HttpServletRequest)request;
        // 输出提示信息
        System.out.println("Filter 已经拦截到用户请求的地址: " + hRequest.getServletPath());

        // Filter 只是链式处理,请求依然放行到目的地址
        chain.doFilter(request, response);

        // 下面代码用于对服务器响应执行后处理
        long after = System.currentTimeMillis();
        // 输出提示信息
        System.out.println("过滤结束");
        System.out.println("请求被定位到" + hRequest.gerRequestURI() + " 所花的时间为:" + (after - before));
    }
}

  

配置 Filter

配置 FIlter 与配置 Servlet 非常相似,都需要配置如下两个部分。

1、配置 Filter 名

2、配置 Filter 拦截 URL 模式

区别在于:Servlet 通常只配置一个 URL,而 Filter 可以同时拦截多个请求的 URL。因此,在配置 Filter 的 URL 模式时通常会使用模式字符串,使得 Filter 可以拦截多个请求。与配置 Servlet 相似的是,配置 Filter 同样有两种方式。

1、在 Filter 中通过注解进行配置

2、在 web.xml 文件中通过配置文件进行配置。

上面 Filter 类使用了 @WebFilter 配置该 Filter 的名字为 log,它会拦截向 /* 发送的所有的请求。

 

@WebFilter 属性:

asyncSupported:指定该 Filter 是否支持异步操作模式。

dispatcherTypes:指定该 Filter 仅对哪种 dispatcher 模式的请求进行过滤。该属性支持 ASYNC、ERROR、FORWARD、INCLUDE、REQUEST 这 5 个值得任意组合。默认为同时过滤 5 种模式的请求。

displayName:指定该 FIlter 的显示名

filterName:指定该 FIlter 的名称

initParams:用于为该 FIlter 配置参数

servletNames:该属性值可指定多个 Servlet 的名称,用于指定该 FIlter 仅对这几个 Servlet 执行过滤

urlPatterns/value:这两个属性的作用完全相同。都指定该 FIlter 所拦截的 URL。

 

web.xml 配置

<!-- 配置 Filter -->
<filter>
  <!-- Filter 的名字,相当于指定 @WebFilter 的 filterName 属性 -->
  <filter-name>log</filter-name>
  <!-- Filter 的实现类 -->
  <filter-class>com.baiguiren.LogFilter</filter-class>
</filter>
<!-- 定义 Filter 拦截的 URL 地址 -->
<filter-mapping>
  <!-- Filter 的名字 -->
  <filter-name>log</filter-name>
  <!-- Filter 负责拦截的 URL,相当于指定 @WebFilter 的 urlPatterns 属性 -->
  <url-pattern>/*</url-pattern>
</filter-mapping>

  

在实际项目中,Filter 里 doFilter() 方法里的代码就是从多个 Servlet 的 service() 方法里抽取的通用代码,通过使用 Filter 可以实现更好的代码复用。

假设系统包含多个 Servlet,这些 Servlet 都需要进行的一些通用处理:比如权限控制、记录日志等,这将导致在这些 Servlet 的 service 方法中有部分代码是相同的 -- 为了解决这种代码重复的问题,可以考虑把这些通用处理提取到 FIlter 中完成,这样各 Servlet 中剩下的只是特定请求相关的处理代码,而通用处理则交给 FIlter 完成。

 

Filter 与 Servlet 具有完全相同的生命周期。

下面将定义一个 Filter,该 Filter 对用户请求进行过滤,Filter 将通过 doFilter 方法来设置 request 编码的字符集,从而避免每个 JSP、Servlet 都需要设置;而且还会验证用户是否登录,没有登录则跳转到登录页:

AuthorityFilter.java

package com.baiguiren;

import java.io.IOException;

import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;

import javax.servlet.annotation.*;

@WebFilter(filterName="authority", urlPatterns={"/*"},
    initParams={
        @WebInitParam(name="encoding", value="UTF-8"),
        @WebInitParam(name="loginPage", value="/login.jsp"),
        @WebInitParam(name="proLogin", value="/proLogin.jsp")
    }
)
public class AuthorityFilter implements Filter
{
    // FilterConfig 可用于访问 Filter 的配置信息
    private FilterConfig config;

    // 实现初始化方法
    public void init(FilterConfig config)
    {
        this.config = config;
    }

    // 实现销毁方法
    public void destroy()
    {
        this.config = null;
    }

    // 执行过滤的核心方法
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
        throws IOException, ServletException
    {
        // 获取该 Filter 的配置参数
        String encoding = config.getInitParameter("encoding");
        String loginPage = config.getInitParameter("loginPage");
        String proLogin = config.getInitParameter("proLogin");

        // 设置 request 字符集
        request.setCharacterEncoding(encoding);
        HttpServletRequest requ = (HttpServletRequest)request;
        HttpSession session = requ.getSession(true);

        // 获取客户端请求的页面
        String requestPath = requ.getServletPath();
        // 如果 session 范围的 user 为 null,即表明没有登录
        // 且用户的请求既不是登录页面,也不是处理登录的页面
        if (session.getAttribute("user") == null
            && !requestPath.endsWith(loginPage)
            && !requestPath.endsWith(proLogin))
        {
            // forward 到登录页
            request.setAttribute("tip", "你还没有登录!");
            request.getRequestDispatcher(loginPage).forward(request, response);
        } else {
            // 放行请求
            chain.doFilter(request, response);
        }
    }
}

  

上面的代码使用注解配置,也可以在 web.xml 中配置:

<filter>
  <filter-name>authority</filter-name>
  <filter-class>com.baiguiren.Authority</filter-class>
  <init-param>
    <param-name>encoding</param-name>
    <param-value>UTF-8</param-value>
  </init-param>
  <init-param>
    <param-name>loginPage</param-name>
    <param-value>/login.jsp</param-value>
  </init-param>
  <init-param>
    <param-name>proLogin</param-name>
    <param-value>/proLogin.jsp</param-value>
  </init-param>
</filter>
<filter-mapping>
  <filter-name>authority</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>

  

上面的配置保证了普通用户只能访问 login.jsp 和 proLogin.jsp,其他页面都需要登录之后才能访问。

 

 

使用 URL Rewrite 实现网站伪静态

对于以 JSP 为表现层开发的动态网站来说,用户访问的 URL 通常有如下形式:

xxx.jsp?name=value...

大部分搜索引擎都会优先考虑收录静态的 HTML 页面,而不是这种动态的 *.jsp、*.php 页面。但实际上大部分网站都是动态的,不可能全部都是静态的 HTML 页面,因此大部分网站都会考虑使用伪静态 -- 就是将 *.jsp 这种动态 URL 伪装成静态的 HTML 页面。

 

对于 JavaWeb 应用来说,要实现这种伪静态非常简单:可以通过 Filter 拦截所有发向 *.html 的请求,然后按某种规则将请求 forward 到实际的 *.jsp 页面即可。

 

实现伪静态步骤:

1、到 http://www.tuckey.org/urlrewrite/ 下载 URL Rewrite 的最新版本

2、把上一步下载的 jar 包放在 WEB-INF/lib 目录下

3、在 web.xml 文件中配置启用 URLRewrite Filter,在 web.xml 中增加如下配置片段

4、在应用的 WEB-INF 路径下增加 urlrewrite.xml 文件,该文件定义了伪静态映射规则,这份伪静态规则是基于正则表达式的

 

urlwrite.xml

<?xml version="1.0" encoding="UTF-8"?>

<urlrewrite>
    <rule>
        <!-- 所有配置如下正则表达式的请求 -->
        <from>/userinf-(\w*).html</from>
        <!-- 将被 forward 到如下 JSP 页面,其中 $1 代表上面第一个正则表达式所匹配的字符串 -->
        <to type="forward">/userinf.jsp?username=$1</to>
    </rule>
</urlrewrite>

  

上面的规则文件中只定义了一个简单的规则:所有发向 /userinf-(\w*).html 的请求都被 forward 到 userinf.jsp 页面,并将 (\w*) 正则表达式所匹配的内容作为 username 参数值。

<%@ page contentType="text/html; charset=UTF-8" %>

<%
// 获取请求参数
String user = request.getParameter("username");
%>

<html>
    <head>
        <title><%=user%>的个人信息</title>
    </head>
    <body>
        <%
            out.println("现在时间是: " + new java.util.Date() + "<br/>");
            out.println("用户名: " + user);
        %>
    </body>
</html>

  

 

posted @ 2018-03-25 22:54  佚名000  阅读(323)  评论(0编辑  收藏  举报