Java Servlet Filter 用于拦截客户端请求并做一些预处理。它还可以在将响应发送到 Web 应用程序中的客户端之前拦截响应并进行后处理。
小服务程序过滤器
在本文中,我们将学习 Java 中的 Servlet 过滤器。我们将研究 servlet 过滤器的各种用法,我们如何创建一个过滤器并通过一个简单的 Web 应用程序了解它的用法。
-
为什么我们有 Servlet 过滤器?
在上一篇文章中,我们学习了如何在 Web 应用程序中管理会话,如果我们想确保只有在用户会话有效时才能访问资源,我们可以使用 servlet 会话属性来实现这一点。方法很简单,但是如果我们有很多 servlet 和 jsps,那么由于冗余代码,它会变得难以维护。如果我们以后想更改属性名称,我们将不得不更改我们进行会话身份验证的所有地方。
这就是我们有一个 servlet 过滤器的原因。Servlet 过滤器是可插入的Java 组件,我们可以使用它们在请求发送到 servlet之前拦截和处理请求,并在servlet 代码完成后以及容器将响应发送回客户端之前进行响应。
我们可以使用 servlet 过滤器完成的一些常见任务是:
- 将请求参数记录到日志文件。
- 资源请求的认证和授权。
- 在将请求正文或标头发送到 servlet 之前对其进行格式化。
- 压缩发送给客户端的响应数据。
- 通过添加一些 cookie、标头信息等来更改响应。
正如我之前提到的,servlet 过滤器是可插入的,并在部署描述符 (web.xml) 文件中进行配置。Servlet 和过滤器彼此不知道,我们可以通过编辑 web.xml 添加或删除 servlet 过滤器。
我们可以为单个资源设置多个过滤器,并且可以在 web.xml 中为单个资源创建一个过滤器链。我们可以通过实现
javax.servlet.Filter
接口来创建一个 Servlet 过滤器。 -
Servlet 过滤器接口
Servlet Filter 接口类似于 Servlet 接口,我们需要实现它来创建我们自己的 servlet 过滤器。Servlet 过滤器接口包含过滤器的生命周期方法,它由 servlet 容器管理。
Servlet Filter 接口生命周期方法有:
- void init(FilterConfig paramFilterConfig) – 当容器初始化过滤器时,这是被调用的方法。这个方法在过滤器的生命周期中只被调用一次,我们应该在这个方法中初始化任何资源。容器使用FilterConfig向 Filter 提供初始化参数和 servlet 上下文对象。我们可以在这个方法中抛出 ServletException。
- doFilter(ServletRequest paramServletRequest, ServletResponse paramServletResponse, FilterChain paramFilterChain) – 这是容器每次必须将过滤器应用于资源时调用的方法。容器提供请求和响应对象引用以过滤作为参数。FilterChain用于调用链中的下一个过滤器。这是责任链模式的一个很好的例子。
- void destroy() – 当容器卸载 Filter 实例时,它会调用 destroy() 方法。这是我们可以关闭过滤器打开的任何资源的方法。此方法在过滤器的生命周期内仅调用一次。
-
Servlet WebFilter 注解
javax.servlet.annotation.WebFilter
在 Servlet 3.0 中引入,我们可以使用这个注解来声明一个 servlet 过滤器。我们可以使用此注解来定义初始化参数、过滤器名称和描述、servlet、url 模式和调度程序类型以应用过滤器。如果您经常更改过滤器配置,最好使用 web.xml,因为这不需要您重新编译过滤器类。阅读:Java 注释教程
-
web.xml 中的 Servlet 过滤器配置
我们可以在 web.xml 中声明一个 servlet 过滤器,如下所示。
<filter> <filter-name>RequestLoggingFilter</filter-name> <!-- mandatory --> <filter-class>com.journaldev.servlet.filters.RequestLoggingFilter</filter-class> <!-- mandatory --> <init-param> <!-- optional --> <param-name>test</param-name> <param-value>testValue</param-value> </init-param> </filter>
我们可以将过滤器映射到 servlet 类或 url 模式,如下所示。
<filter-mapping> <filter-name>RequestLoggingFilter</filter-name> <!-- mandatory --> <url-pattern>/*</url-pattern> <!-- either url-pattern or servlet-name is mandatory --> <servlet-name>LoginServlet</servlet-name> <dispatcher>REQUEST</dispatcher> </filter-mapping>
注意:在为 servlet 创建过滤器链时,容器首先处理 url-patterns,然后是 servlet-names,因此如果您必须确保过滤器按特定顺序执行,请在定义过滤器映射时特别注意。
Servlet 过滤器通常用于客户端请求,但有时我们也想使用RequestDispatcher来应用过滤器,在这种情况下我们可以使用调度程序元素,可能的值是 REQUEST、FORWARD、INCLUDE、ERROR 和 ASYNC。如果未定义分派器,则它仅应用于客户端请求。
-
用于记录和会话验证的 Servlet 过滤器示例
在我们的servlet 过滤器示例中,我们将创建过滤器来记录请求 cookie 和参数,并验证除静态 HTML 和 LoginServlet 之外的所有资源的会话,因为它不会有会话。
我们将创建一个动态 Web 项目ServletFilterExample,其项目结构如下图所示。
login.html 是我们应用程序的入口点,用户将在其中提供登录 ID 和密码以进行身份验证。
login.html 代码:
<!DOCTYPE html> <html> <head> <meta charset="US-ASCII"> <title>Login Page</title> </head> <body> <form action="LoginServlet" method="post"> Username: <input type="text" name="user"> <br> Password: <input type="password" name="pwd"> <br> <input type="submit" value="Login"> </form> </body> </html>
LoginServlet 用于验证来自客户端的登录请求。
package com.journaldev.servlet.session; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.RequestDispatcher; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; /** * Servlet implementation class LoginServlet */ @WebServlet("/LoginServlet") public class LoginServlet extends HttpServlet { private static final long serialVersionUID = 1L; private final String userID = "admin"; private final String password = "password"; protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // get request parameters for userID and password String user = request.getParameter("user"); String pwd = request.getParameter("pwd"); if(userID.equals(user) && password.equals(pwd)){ HttpSession session = request.getSession(); session.setAttribute("user", "Pankaj"); //setting session to expiry in 30 mins session.setMaxInactiveInterval(30*60); Cookie userName = new Cookie("user", user); userName.setMaxAge(30*60); response.addCookie(userName); response.sendRedirect("LoginSuccess.jsp"); }else{ RequestDispatcher rd = getServletContext().getRequestDispatcher("/login.html"); PrintWriter out= response.getWriter(); out.println("<font color=red>Either user name or password is wrong.</font>"); rd.include(request, response); } } }
当客户端通过身份验证时,它被转发到 LoginSuccess.jsp
LoginSuccess.jsp 代码:
<%@ page language="java" contentType="text/html; charset=US-ASCII" pageEncoding="US-ASCII"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "https://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=US-ASCII"> <title>Login Success Page</title> </head> <body> <% //allow access only if session exists String user = (String) session.getAttribute("user"); String userName = null; String sessionID = null; Cookie[] cookies = request.getCookies(); if(cookies !=null){ for(Cookie cookie : cookies){ if(cookie.getName().equals("user")) userName = cookie.getValue(); if(cookie.getName().equals("JSESSIONID")) sessionID = cookie.getValue(); } } %> <h3>Hi <%=userName %>, Login successful. Your Session ID=<%=sessionID %></h3> <br> User=<%=user %> <br> <a href="CheckoutPage.jsp">Checkout Page</a> <form action="LogoutServlet" method="post"> <input type="submit" value="Logout" > </form> </body> </html>
请注意,上述 JSP 中没有会话验证逻辑。它包含指向另一个 JSP 页面 CheckoutPage.jsp 的链接。
CheckoutPage.jsp 代码:
<%@ page language="java" contentType="text/html; charset=US-ASCII" pageEncoding="US-ASCII"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "https://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=US-ASCII"> <title>Login Success Page</title> </head> <body> <% String userName = null; String sessionID = null; Cookie[] cookies = request.getCookies(); if(cookies !=null){ for(Cookie cookie : cookies){ if(cookie.getName().equals("user")) userName = cookie.getValue(); } } %> <h3>Hi <%=userName %>, do the checkout.</h3> <br> <form action="LogoutServlet" method="post"> <input type="submit" value="Logout" > </form> </body> </html>
当客户端单击任何 JSP 页面中的 Logout 按钮时,将调用 LogoutServlet。
package com.journaldev.servlet.session; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; /** * Servlet implementation class LogoutServlet */ @WebServlet("/LogoutServlet") public class LogoutServlet extends HttpServlet { private static final long serialVersionUID = 1L; protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html"); Cookie[] cookies = request.getCookies(); if(cookies != null){ for(Cookie cookie : cookies){ if(cookie.getName().equals("JSESSIONID")){ System.out.println("JSESSIONID="+cookie.getValue()); break; } } } //invalidate the session if exists HttpSession session = request.getSession(false); System.out.println("User="+session.getAttribute("user")); if(session != null){ session.invalidate(); } response.sendRedirect("login.html"); } }
现在我们将创建日志记录和身份验证 servlet 过滤器类。
package com.journaldev.servlet.filters; import java.io.IOException; import java.util.Enumeration; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.annotation.WebFilter; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; /** * Servlet Filter implementation class RequestLoggingFilter */ @WebFilter("/RequestLoggingFilter") public class RequestLoggingFilter implements Filter { private ServletContext context; public void init(FilterConfig fConfig) throws ServletException { this.context = fConfig.getServletContext(); this.context.log("RequestLoggingFilter initialized"); } public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest) request; Enumeration<String> params = req.getParameterNames(); while(params.hasMoreElements()){ String name = params.nextElement(); String value = request.getParameter(name); this.context.log(req.getRemoteAddr() + "::Request Params::{"+name+"="+value+"}"); } Cookie[] cookies = req.getCookies(); if(cookies != null){ for(Cookie cookie : cookies){ this.context.log(req.getRemoteAddr() + "::Cookie::{"+cookie.getName()+","+cookie.getValue()+"}"); } } // pass the request along the filter chain chain.doFilter(request, response); } public void destroy() { //we can close resources here } }
package com.journaldev.servlet.filters; import java.io.IOException; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.annotation.WebFilter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; @WebFilter("/AuthenticationFilter") public class AuthenticationFilter implements Filter { private ServletContext context; public void init(FilterConfig fConfig) throws ServletException { this.context = fConfig.getServletContext(); this.context.log("AuthenticationFilter initialized"); } public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest) request; HttpServletResponse res = (HttpServletResponse) response; String uri = req.getRequestURI(); this.context.log("Requested Resource::"+uri); HttpSession session = req.getSession(false); if(session == null && !(uri.endsWith("html") || uri.endsWith("LoginServlet"))){ this.context.log("Unauthorized access request"); res.sendRedirect("login.html"); }else{ // pass the request along the filter chain chain.doFilter(request, response); } } public void destroy() { //close any resources here } }
请注意,我们没有对任何 HTML 页面或 LoginServlet 进行身份验证。现在我们将在 web.xml 文件中配置这些过滤器映射。
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xmlns="https://java.sun.com/xml/ns/javaee" xsi:schemaLocation="https://java.sun.com/xml/ns/javaee https://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0"> <display-name>ServletFilterExample</display-name> <welcome-file-list> <welcome-file>login.html</welcome-file> </welcome-file-list> <filter> <filter-name>RequestLoggingFilter</filter-name> <filter-class>com.journaldev.servlet.filters.RequestLoggingFilter</filter-class> </filter> <filter> <filter-name>AuthenticationFilter</filter-name> <filter-class>com.journaldev.servlet.filters.AuthenticationFilter</filter-class> </filter> <filter-mapping> <filter-name>RequestLoggingFilter</filter-name> <url-pattern>/*</url-pattern> <dispatcher>REQUEST</dispatcher> </filter-mapping> <filter-mapping> <filter-name>AuthenticationFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> </web-app>
现在,当我们运行我们的应用程序时,我们将获得如下图所示的响应页面。
如果您没有登录并尝试访问任何 JSP 页面,您将被转发到登录页面。
在服务器日志文件中,您可以看到 servlet 过滤器以及 servlet 写入的日志。
Aug 13, 2013 1:06:07 AM org.apache.catalina.core.ApplicationContext log INFO: 0:0:0:0:0:0:0:1%0::Cookie::{JSESSIONID,B7275762B8D23121152B1270D6EB240A} Aug 13, 2013 1:06:07 AM org.apache.catalina.core.ApplicationContext log INFO: Requested Resource::/ServletFilterExample/ Aug 13, 2013 1:06:07 AM org.apache.catalina.core.ApplicationContext log INFO: Unauthorized access request Aug 13, 2013 1:06:07 AM org.apache.catalina.core.ApplicationContext log INFO: 0:0:0:0:0:0:0:1%0::Cookie::{JSESSIONID,B7275762B8D23121152B1270D6EB240A} Aug 13, 2013 1:06:07 AM org.apache.catalina.core.ApplicationContext log INFO: Requested Resource::/ServletFilterExample/login.html Aug 13, 2013 1:06:43 AM org.apache.catalina.core.ApplicationContext log INFO: 0:0:0:0:0:0:0:1%0::Request Params::{pwd=password} Aug 13, 2013 1:06:43 AM org.apache.catalina.core.ApplicationContext log INFO: 0:0:0:0:0:0:0:1%0::Request Params::{user=admin} Aug 13, 2013 1:06:43 AM org.apache.catalina.core.ApplicationContext log INFO: 0:0:0:0:0:0:0:1%0::Cookie::{JSESSIONID,B7275762B8D23121152B1270D6EB240A} Aug 13, 2013 1:06:43 AM org.apache.catalina.core.ApplicationContext log INFO: Requested Resource::/ServletFilterExample/LoginServlet Aug 13, 2013 1:06:43 AM org.apache.catalina.core.ApplicationContext log INFO: 0:0:0:0:0:0:0:1%0::Cookie::{JSESSIONID,8BDF777933194EDCAC1D8F1B73633C56} Aug 13, 2013 1:06:43 AM org.apache.catalina.core.ApplicationContext log INFO: 0:0:0:0:0:0:0:1%0::Cookie::{user,admin} Aug 13, 2013 1:06:43 AM org.apache.catalina.core.ApplicationContext log INFO: Requested Resource::/ServletFilterExample/LoginSuccess.jsp Aug 13, 2013 1:06:52 AM org.apache.catalina.core.ApplicationContext log INFO: 0:0:0:0:0:0:0:1%0::Cookie::{JSESSIONID,8BDF777933194EDCAC1D8F1B73633C56} Aug 13, 2013 1:06:52 AM org.apache.catalina.core.ApplicationContext log INFO: 0:0:0:0:0:0:0:1%0::Cookie::{user,admin} Aug 13, 2013 1:06:52 AM org.apache.catalina.core.ApplicationContext log INFO: Requested Resource::/ServletFilterExample/CheckoutPage.jsp Aug 13, 2013 1:07:00 AM org.apache.catalina.core.ApplicationContext log INFO: 0:0:0:0:0:0:0:1%0::Cookie::{JSESSIONID,8BDF777933194EDCAC1D8F1B73633C56} Aug 13, 2013 1:07:00 AM org.apache.catalina.core.ApplicationContext log INFO: 0:0:0:0:0:0:0:1%0::Cookie::{user,admin} Aug 13, 2013 1:07:00 AM org.apache.catalina.core.ApplicationContext log INFO: Requested Resource::/ServletFilterExample/LogoutServlet JSESSIONID=8BDF777933194EDCAC1D8F1B73633C56 User=Pankaj Aug 13, 2013 1:07:00 AM org.apache.catalina.core.ApplicationContext log INFO: 0:0:0:0:0:0:0:1%0::Cookie::{JSESSIONID,8BDF777933194EDCAC1D8F1B73633C56} Aug 13, 2013 1:07:00 AM org.apache.catalina.core.ApplicationContext log INFO: 0:0:0:0:0:0:0:1%0::Cookie::{user,admin} Aug 13, 2013 1:07:00 AM org.apache.catalina.core.ApplicationContext log INFO: Requested Resource::/ServletFilterExample/login.html Aug 13, 2013 1:07:06 AM org.apache.catalina.core.ApplicationContext log INFO: 0:0:0:0:0:0:0:1%0::Cookie::{JSESSIONID,8BDF777933194EDCAC1D8F1B73633C56} Aug 13, 2013 1:07:07 AM org.apache.catalina.core.ApplicationContext log INFO: 0:0:0:0:0:0:0:1%0::Cookie::{user,admin} Aug 13, 2013 1:07:07 AM org.apache.catalina.core.ApplicationContext log INFO: Requested Resource::/ServletFilterExample/LoginSuccess.jsp Aug 13, 2013 1:07:07 AM org.apache.catalina.core.ApplicationContext log INFO: Unauthorized access request Aug 13, 2013 1:07:07 AM org.apache.catalina.core.ApplicationContext log INFO: 0:0:0:0:0:0:0:1%0::Cookie::{JSESSIONID,8BDF777933194EDCAC1D8F1B73633C56} Aug 13, 2013 1:07:07 AM org.apache.catalina.core.ApplicationContext log INFO: 0:0:0:0:0:0:0:1%0::Cookie::{user,admin} Aug 13, 2013 1:07:07 AM org.apache.catalina.core.ApplicationContext log INFO: Requested Resource::/ServletFilterExample/login.html
这就是java中的Servlet过滤器的全部内容。它是 Java EE Web 应用程序的重要特性之一,我们应该将它用于各种 servlet 执行的常见任务。在以后的文章中,我们将研究 servlet 侦听器和 cookie。
更新:在收到大量可下载项目的请求后,我已将其附加到帖子中,从下面的链接下载。
查看有关Servlet 侦听器的系列中的下一篇文章。
更新
Struts 2 使用 Servlet Filter 来拦截客户端请求并将它们转发到适当的操作类,这些被称为 Struts 2 拦截器。查看Struts 2 初学者教程。[no_toc]