zoukankan      html  css  js  c++  java
  • 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>
    

      

  • 相关阅读:
    java 标准异常
    java 重新抛出异常
    java 异常链
    java 轨迹栈
    mysql死锁-非主键索引更新引起的死锁
    数据库事务
    JMS学习笔记(一)
    log4j中将SocketAppender将日志内容发送到远程服务器
    Kubernetes之kubectl常用命令
    java代理与动态代理的学习
  • 原文地址:https://www.cnblogs.com/eleven24/p/8647557.html
Copyright © 2011-2022 走看看