Updated on 2017-04-03

工作原理

用于 拦截 客户端的 请求 信息和服务端的 响应 信息,并对这些信息进行 过滤

MyFilter1

package a

import javax.servlet.*

class MyFilter1 : Filter {     继承 Filter
    override fun init(filterConfig: FilterConfig) {     用于读取 web.xml 中过滤器的配置参数<init-param>
        println("init---MyFilter1")
    }

    override fun doFilter(p0: ServletRequest, p1: ServletResponse, p2: FilterChain) {     核心方法
        println("doFilter---MyFilter1---Start")     放行前
        p2.doFilter(p0, p1)     不过滤放行):将请求传递给下一个过滤器若已为最后一个过滤器则为目标资源
        println("doFilter---MyFilter1---End")     放行后

        要过滤通过请求转发request)、请求重定向response),跳转至其他资源
    }

    override fun destroy() {     用于释放过滤器所占用的资源
        println("destroy---MyFilter1")
    }
}

MyFilter2

package a

import javax.servlet.*

class MyFilter2 : Filter {
    override fun init(filterConfig: FilterConfig) {
        println("init---MyFilter2")
    }

    override fun doFilter(p0: ServletRequest, p1: ServletResponse, p2: FilterChain) {
        println("doFilter---MyFilter2---Start")
        p2.doFilter(p0, p1)
        println("doFilter---MyFilter2---End")
    }

    override fun destroy() {
        println("destroy---MyFilter2")
    }
}

index.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%
    System.out.println("index.jsp");
%>
<html>
<head>
    <title>Title</title>
</head>
<body>
</body>
</html>

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1">

    <filter>     过滤器
        <filter-name>f1</filter-name>
        <filter-class>a.MyFilter1</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>f1</filter-name>
        <url-pattern>/*</url-pattern>     匹配所有路径
    </filter-mapping>

    <filter>     过滤器
        <filter-name>f2</filter-name>
        <filter-class>a.MyFilter2</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>f2</filter-name>
        <url-pattern>/*</url-pattern>     匹配所有路径
    </filter-mapping>

</web-app>

过滤器链:若一个请求匹配有多个过滤器,则服务器会按照 web.xml 中定义过滤器的先后顺序将过滤器组装成一条链。

Output

init---MyFilter1
init---MyFilter2

doFilter---MyFilter1---Start
doFilter---MyFilter2---Start
index.jsp
doFilter---MyFilter2---End
doFilter---MyFilter1---End

destroy---MyFilter1
destroy---MyFilter2

Dispatcher

  • REQUEST:当目标资源是通过 用户直接访问 时,将调用该过滤器。(缺省值)
  • FORWARD:当目标资源是通过 **RequestDispatcher.forward()**访问时,将调用该过滤器。
  • INCLUDE:当目标资源是通过 RequestDispatcher.include() 访问时,将调用该过滤器。
  • ERROR:当目标资源是通过 异常处理机制 访问时,将调用该过滤器。
  • ASYNC:支持异步处理。

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1">

    <filter>
        <filter-name>f1</filter-name>
        <filter-class>a.MyFilter1</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>f1</filter-name>
        <url-pattern>/error.jsp</url-pattern>
        <dispatcher>ERROR</dispatcher>     [REQUEST, FORWARD, INCLUDE, ERROR, ASYNC]
    </filter-mapping>                          ↳ 若未配置 dispatcher,则 REQUEST 为缺省值

    <error-page>
        <location>/error.jsp</location>
    </error-page>

</web-app>

直接访问 http://localhost:8080/error.jsp,对 f1 过滤器无效(用户直接访问)
直接访问 http://localhost:8080/15742.jsp,对 f1 过滤器有效(异常处理机制,404)

MyFilter1

package a

import javax.servlet.*
import javax.servlet.annotation.WebFilter

@WebFilter(urlPatterns = arrayOf("/error.jsp"), dispatcherTypes = arrayOf(DispatcherType.ERROR))     //通过注解方式配置过滤器
class MyFilter1 : Filter {
    override fun init(filterConfig: FilterConfig) {}

    override fun doFilter(p0: ServletRequest, p1: ServletResponse, p2: FilterChain) {
        println("发生错误!")
        p2.doFilter(p0, p1)     放行
    }

    override fun destroy() {}
}

error.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>错误页面</title>
</head>
<body>
<h1>错误页面</h1>
<hr>
</body>
</html>

登录页面案例

JSP

index.jsp
----
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>登录页面</title>
</head>
<body>
<h1>登录页面</h1>
<hr>
<form action="doLogin.jsp" name="loginForm" method="post">
    <table>
        <tr>
            <td>账号:</td>
            <td><input type="text" name="username"></td>
        </tr>
        <tr>
            <td>密码:</td>
            <td><input type="password" name="password"></td>
        </tr>
        <tr>
            <td><input type="reset" value="重置"></td>
            <td><input type="submit" value="登录"></td>
        </tr>
    </table>
</form>
</body>
</html>

doLogin.jsp
----
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%
    request.setCharacterEncoding("UTF-8");
    String username = request.getParameter("username");
    String password = request.getParameter("password");
    if ("admin".equals(username) && "123456".equals(password)) {
        session.setAttribute("username", username);
        response.sendRedirect("login_successful.jsp");
    } else {
        response.sendRedirect("login_failed.jsp");
    }
%>

login_successful.jsp
----
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>登录成功</title>
</head>
<body>
<h1>登录成功</h1>
<hr>
欢迎用户:${username}     EL 表达式
</body>
</html>

login_failed.jsp
----
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>登录失败</title>
</head>
<body>
<h1>登录失败</h1>
<hr>
<a href="/">返回登录页面</a>
</body>
</html>

MyFilter

class MyFilter : Filter {
    lateinit var noFilterList: List<String>

    override fun init(filterConfig: FilterConfig) {
        noFilterList = filterConfig.getInitParameter("noFilterList").split(";")     获取放行关键字
    }

    override fun doFilter(request: ServletRequest, response: ServletResponse, chain: FilterChain) {
        val request1 = request as HttpServletRequest
        val response1 = response as HttpServletResponse
        request1.characterEncoding = "UTF-8"     统一指定输入字符集

        val uri = request1.requestURI     请求路径
        if (request1.session.getAttribute("username") != null || uri == "/" || check(uri)) {
            chain.doFilter(request, response)     放行通过
        } else {
            response1.sendRedirect("/")     不放行重定向至根目录
        }
    }

    override fun destroy() {
    }

    private fun check(s: String) = noFilterList.find { s.contains(it) } != null     若请求路径包含某个关键字则返回 true
}

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
         version="3.1">

    <filter>
        <filter-name>f</filter-name>
        <filter-class>a.MyFilter</filter-class>
        <init-param>
            <param-name>noFilterList</param-name>     放行关键字
            <param-value>index.jsp;doLogin.jsp;login_failed.jsp</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>f</filter-name>
        <url-pattern>/*</url-pattern>     匹配所有路径
    </filter-mapping>

</web-app>