JSP 和 Servlet 中的过滤器都是 Java 类。
过滤器可以动态地拦截请求和响应,以变换或使用包含在请求或响应中的信息。
可以将一个或多个过滤器附加到一个 Servlet 或一组 Servlet。过滤器也可以附加到 JavaServer Pages (JSP) 文件和 HTML 页面。
过滤器是可用于 Servlet 编程的 Java 类,可以实现以下目的:
在客户端的请求访问后端资源之前,拦截这些请求。在服务器的响应发送回客户端之前,处理这些响应。根据规范建议的各种类型的过滤器:身份验证过滤器(Authentication Filters)。数据压缩过滤器(Data compression Filters)。加密过滤器(Encryption Filters)。触发资源访问事件过滤器。图像转换过滤器(Image Conversion Filters)。日志记录和审核过滤器(Logging and Auditing Filters)。MIME-TYPE 链过滤器(MIME-TYPE Chain Filters)。标记化过滤器(Tokenizing Filters)。XSL/T 过滤器(XSL/T Filters),转换 XML 内容。过滤器通过 Web 部署描述符(web.xml)中的 XML 标签来声明,然后映射到您的应用程序的部署描述符中的 Servlet 名称或 URL 模式。
当 Web 容器启动 Web 应用程序时,它会为您在部署描述符中声明的每一个过滤器创建一个实例。
Filter 的执行顺序与在 web.xml 配置文件中的配置顺序一致,一般把 Filter 配置在所有的 Servlet 之前。
过滤器是一个实现了 javax.servlet.Filter 接口的 Java 类。javax.servlet.Filter 接口定义了三个方法:
方法 & 描述
public void doFilter (ServletRequest, ServletResponse, FilterChain)该方法完成实际的过滤操作,当客户端请求方法与过滤器设置匹配的URL时,Servlet 容器将先调用过滤器的 doFilter 方法。FilterChain 用于访问后续过滤器。public void init(FilterConfig filterConfig)web 应用程序启动时,web 服务器将创建Filter 的实例对象,并调用其init方法,读取web.xml配置,完成对象的初始化功能,从而为后续的用户请求作好拦截的准备工作(filter对象只会创建一次,init方法也只会执行一次)。开发人员通过init方法的参数,可获得代表当前filter配置信息的FilterConfig对象。public void destroy()Servlet容器在销毁过滤器实例前调用该方法,在该方法中释放Servlet过滤器占用的资源。Filter 的 init 方法中提供了一个 FilterConfig 对象。 如 web.xml 文件配置如下:
<filter> <filter-name>LogFilter</filter-name> <filter-class>com.runoob.test.LogFilter</filter-class> <init-param> <param-name>Site</param-name> <param-value>菜鸟教程</param-value> </init-param> </filter>在 init 方法使用 FilterConfig 对象获取参数:
public void init(FilterConfig config) throws ServletException { // 获取初始化参数 String site = config.getInitParameter("Site"); // 输出初始化参数 System.out.println("网站名称: " + site); }以下是 Servlet 过滤器的实例,将输出网站名称和地址。本实例让您对 Servlet 过滤器有基本的了解,您可以使用相同的概念编写更复杂的过滤器应用程序:
//导入必需的 java 库 import javax.servlet.*; import java.util.*; //实现 Filter 类 public class LogFilter implements Filter { public void init(FilterConfig config) throws ServletException { // 获取初始化参数 String site = config.getInitParameter("Site"); // 输出初始化参数 System.out.println("网站名称: " + site); } public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws java.io.IOException, ServletException { // 输出站点名称 System.out.println("站点网址:http://www.runoob.com"); // 把请求传回过滤链 chain.doFilter(request,response); } public void destroy( ){ /* 在 Filter 实例被 Web 容器从服务移除之前调用 */ } }DisplayHeader.java 文件代码如下:
//导入必需的 java 库 import java.io.IOException; import java.io.PrintWriter; import java.util.Enumeration; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @WebServlet("/DisplayHeader") //扩展 HttpServlet 类 public class DisplayHeader extends HttpServlet { // 处理 GET 方法请求的方法 public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // 设置响应内容类型 response.setContentType("text/html;charset=UTF-8"); PrintWriter out = response.getWriter(); String title = "HTTP Header 请求实例 - 菜鸟教程实例"; String docType = "<!DOCTYPE html> \n"; out.println(docType + "<html>\n" + "<head><meta charset=\"utf-8\"><title>" + title + "</title></head>\n"+ "<body bgcolor=\"#f0f0f0\">\n" + "<h1 align=\"center\">" + title + "</h1>\n" + "<table width=\"100%\" border=\"1\" align=\"center\">\n" + "<tr bgcolor=\"#949494\">\n" + "<th>Header 名称</th><th>Header 值</th>\n"+ "</tr>\n"); Enumeration headerNames = request.getHeaderNames(); while(headerNames.hasMoreElements()) { String paramName = (String)headerNames.nextElement(); out.print("<tr><td>" + paramName + "</td>\n"); String paramValue = request.getHeader(paramName); out.println("<td> " + paramValue + "</td></tr>\n"); } out.println("</table>\n</body></html>"); } // 处理 POST 方法请求的方法 public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doGet(request, response); } }Web.xml 中的 Servlet 过滤器映射(Servlet Filter Mapping) 定义过滤器,然后映射到一个 URL 或 Servlet,这与定义 Servlet,然后映射到一个 URL 模式方式大致相同。在部署描述符文件 web.xml 中为 filter 标签创建下面的条目:
<?xml version="1.0" encoding="UTF-8"?> <web-app> <filter> <filter-name>LogFilter</filter-name> <filter-class>com.runoob.test.LogFilter</filter-class> <init-param> <param-name>Site</param-name> <param-value>菜鸟教程</param-value> </init-param> </filter> <filter-mapping> <filter-name>LogFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <servlet> <!-- 类名 --> <servlet-name>DisplayHeader</servlet-name> <!-- 所在的包 --> <servlet-class>com.runoob.test.DisplayHeader</servlet-class> </servlet> <servlet-mapping> <servlet-name>DisplayHeader</servlet-name> <!-- 访问的网址 --> <url-pattern>/TomcatTest/DisplayHeader</url-pattern> </servlet-mapping> </web-app>上述过滤器适用于所有的 Servlet,因为我们在配置中指定 /* 。如果您只想在少数的 Servlet 上应用过滤器,您可以指定一个特定的 Servlet 路径。 现在试着以常用的方式调用任何 Servlet,您将会看到在 Web 服务器中生成的日志。您也可以使用 Log4J 记录器来把上面的日志记录到一个单独的文件中。 接下来我们访问这个实例地址 http://localhost:8080/TomcatTest/DisplayHeader, 然后在控制台看下输出内容,如下所示:
使用多个过滤器 Web 应用程序可以根据特定的目的定义若干个不同的过滤器。假设您定义了两个过滤器 AuthenFilter 和 LogFilter。您需要创建一个如下所述的不同的映射,其余的处理与上述所讲解的大致相同:
<filter> <filter-name>LogFilter</filter-name> <filter-class>com.runoob.test.LogFilter</filter-class> <init-param> <param-name>test-param</param-name> <param-value>Initialization Paramter</param-value> </init-param> </filter> <filter> <filter-name>AuthenFilter</filter-name> <filter-class>com.runoob.test.AuthenFilter</filter-class> <init-param> <param-name>test-param</param-name> <param-value>Initialization Paramter</param-value> </init-param> </filter> <filter-mapping> <filter-name>LogFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <filter-mapping> <filter-name>AuthenFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>过滤器的应用顺序 web.xml 中的 filter-mapping 元素的顺序决定了 Web 容器应用过滤器到 Servlet 的顺序。若要反转过滤器的顺序,您只需要在 web.xml 文件中反转 filter-mapping 元素即可。 例如,上面的实例将先应用 LogFilter,然后再应用 AuthenFilter,但是下面的实例将颠倒这个顺序:
<filter-mapping> <filter-name>AuthenFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <filter-mapping> <filter-name>LogFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>Cookie 是存储在客户机的文本文件,它们保存了大量轨迹信息。在 Servlet 技术基础上,JSP 显然能够提供对 HTTP cookie 的支持。 通常有三个步骤来识别回头客:
服务器脚本发送一系列 cookie 至浏览器。比如名字,年龄,ID 号码等等。浏览器在本地机中存储这些信息,以备不时之需。当下一次浏览器发送任何请求至服务器时,它会同时将这些 cookie 信息发送给服务器,然后服务器使用这些信息来识别用户或者干些其它事情。本章节将会传授您如何去设置或重设 cookie 的方法,还有如何访问它们及如何删除它们。 JSP Cookie 处理需要对中文进行编码与解码,方法如下:
String str = java.net.URLEncoder.encode("中文", "UTF-8"); //编码 String str = java.net.URLDecoder.decode("编码后的字符串","UTF-8"); // 解码使用 JSP 设置 cookie 使用 JSP 设置 cookie 包含三个步骤: (1)创建一个 cookie 对象: 调用 cookie 的构造函数,使用一个 cookie 名称和值做参数,它们都是字符串。
Cookie cookie = new Cookie("key","value");请务必牢记,名称和值中都不能包含空格或者如下的字符: [ ] ( ) = , " / ? @ : ;
(2) 设置有效期:调用 setMaxAge() 函数表明 cookie 在多长时间(以秒为单位)内有效。下面的操作将有效期设为了 24 小时。
cookie.setMaxAge(60*60*24);(3) 将 cookie 发送至 HTTP 响应头中:调用 response.addCookie() 函数来向 HTTP 响应头中添加 cookie。
response.addCookie(cookie);实例演示 main.jsp 文件代码如下所示:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ page import="java.net.*" %> <% // 编码,解决中文乱码 String str = URLEncoder.encode(request.getParameter("name"),"utf-8"); // 设置 name 和 url cookie Cookie name = new Cookie("name", str); Cookie url = new Cookie("url", request.getParameter("url")); // 设置cookie过期时间为24小时。 name.setMaxAge(60*60*24); url.setMaxAge(60*60*24); // 在响应头部添加cookie response.addCookie( name ); response.addCookie( url ); %> <html> <head> <title>设置 Cookie</title> </head> <body> <h1>设置 Cookie</h1> <ul> <li><p><b>网站名:</b> <%= request.getParameter("name")%> </p></li> <li><p><b>网址:</b> <%= request.getParameter("url")%> </p></li> </ul> </body> </html>以下是一个简单的 HTML 表单通过 GET 方法将客户端数据提交到 main.jsp 文件中,并设置 cookie:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>菜鸟教程(runoob.com)</title> </head> <body> <form action="main.jsp" method=GET> 站点名: <input type="text" name="name"> <br /> 网址: <input type="text" name="url" /> <input type="submit" value="提交" /> </form> </body> </html>将以上 HTML 代码保存到 test.htm 文件中。 将该文件放置于当前 jsp 项目的 WebContent 目录下(与 main.jsp 同一个目录)。 通过访问 http://localhost:8080/testjsp/test.html 提交表单数据到 main.jsp 文件,演示 Gif 图如下所示:
试着输入 “站点名” 和 “网址”,然后点击提交按钮,它将会在您的屏幕中显示 “站点名” 和 “网址”,并且设置 “站点名” 和 “网址” 的两个 cookie。
使用 JSP 读取 Cookie 想要读取 cookie,您就需要调用 request.getCookies() 方法来获得一个 javax.servlet.http.Cookie 对象的数组,然后遍历这个数组,使用 getName() 方法和 getValue() 方法来获取每一个 cookie 的名称和值。 让我们来读取上个例子中的cookie, 以下为 cookie.jsp 文件代码:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ page import="java.net.*" %> <!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>获取 Cookie</title> </head> <body> <% Cookie cookie = null; Cookie[] cookies = null; // 获取 cookies 的数据,是一个数组 cookies = request.getCookies(); if( cookies != null ){ out.println("<h2> 查找 Cookie 名与值</h2>"); for (int i = 0; i < cookies.length; i++){ cookie = cookies[i]; out.print("参数名 : " + cookie.getName()); out.print("<br>"); out.print("参数值: " + URLDecoder.decode(cookie.getValue(), "utf-8") +" <br>"); out.print("------------------------------------<br>"); } }else{ out.println("<h2>没有发现 Cookie</h2>"); } %> </body> </html>浏览器访问后,输出结果为:
使用 JSP 删除 cookie 删除 cookie 非常简单。如果您想要删除一个 cookie,按照下面给的步骤来做就行了: 获取一个已经存在的 cookie 然后存储在 Cookie 对象中。 将 cookie 的有效期设置为 0。 将这个 cookie 重新添加进响应头中。
实例演示 下面的程序删除一个名为 “name” 的 cookie,当您第二次运行 cookie.jsp时,name 将会为 null。
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ page import="java.net.*" %> <!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>获取 Cookie</title> </head> <body> <% Cookie cookie = null; Cookie[] cookies = null; // 获取当前域名下的cookies,是一个数组 cookies = request.getCookies(); if( cookies != null ){ out.println("<h2> 查找 Cookie 名与值</h2>"); for (int i = 0; i < cookies.length; i++){ cookie = cookies[i]; if((cookie.getName( )).compareTo("name") == 0 ){ cookie.setMaxAge(0); response.addCookie(cookie); out.print("删除 Cookie: " + cookie.getName( ) + "<br/>"); } out.print("参数名 : " + cookie.getName()); out.print("<br>"); out.print("参数值: " + URLDecoder.decode(cookie.getValue(), "utf-8") +" <br>"); out.print("------------------------------------<br>"); } }else{ out.println("<h2>没有发现 Cookie</h2>"); } %> </body> </html>HTTP是无状态协议,这意味着每次客户端检索网页时,都要单独打开一个服务器连接,因此服务器不会记录下先前客户端请求的任何信息。 有三种方法来维持客户端与服务器的会话:
Cookies 网络服务器可以指定一个唯一的session ID作为cookie来代表每个客户端,用来识别这个客户端接下来的请求。 这可能不是一种有效的方式,因为很多时候浏览器并不一定支持cookie,所以我们不建议使用这种方法来维持会话。
隐藏表单域 一个网络服务器可以发送一个隐藏的HTML表单域和一个唯一的session ID,就像下面这样:
<input type="hidden" name="sessionid" value="12345">这个条目意味着,当表单被提交时,指定的名称和值将会自动包含在GET或POST数据中。每当浏览器发送一个请求,session_id的值就可以用来保存不同浏览器的轨迹。 这种方式可能是一种有效的方式,但点击标签中的超链接时不会产生表单提交事件,因此隐藏表单域也不支持通用会话跟踪。
重写URL 您可以在每个URL后面添加一些额外的数据来区分会话,服务器能够根据这些数据来关联session标识符。 举例来说,http://w3cschool.cc/file.htm;sessionid=12345, session标识符为sessionid=12345,服务器可以用这个数据来识别客户端。 相比而言,重写URL是更好的方式来,就算浏览器不支持cookies也能工作,但缺点是您必须为每个URL动态指定session ID,就算这是个简单的HTML页面。
除了以上几种方法外,JSP利用servlet提供的HttpSession接口来识别一个用户,存储这个用户的所有访问信息。 默认情况下,JSP允许会话跟踪,一个新的HttpSession对象将会自动地为新的客户端实例化。禁止会话跟踪需要显式地关掉它,通过将page指令中session属性值设为false来实现,就像下面这样:
<%@ page session="false" %>JSP引擎将隐含的session对象暴露给开发者。由于提供了session对象,开发者就可以方便地存储或检索数据。 下表列出了session对象的一些重要方法: 方法 & 描述
public Object getAttribute(String name)返回session对象中与指定名称绑定的对象,如果不存在则返回nullpublic Enumeration getAttributeNames()返回session对象中所有的对象名称public long getCreationTime()返回session对象被创建的时间, 以毫秒为单位,从1970年1月1号凌晨开始算起public String getId()返回session对象的IDpublic long getLastAccessedTime()返回客户端最后访问的时间,以毫秒为单位,从1970年1月1号凌晨开始算起public int getMaxInactiveInterval()返回最大时间间隔,以秒为单位,servlet 容器将会在这段时间内保持会话打开public void invalidate()将session无效化,解绑任何与该session绑定的对象public boolean isNew()返回是否为一个新的客户端,或者客户端是否拒绝加入sessionpublic void removeAttribute(String name)移除session中指定名称的对象public void setAttribute(String name, Object value) 使用指定的名称和值来产生一个对象并绑定到session中public void setMaxInactiveInterval(int interval)用来指定时间,以秒为单位,servlet容器将会在这段时间内保持会话有效这个例子描述了如何使用HttpSession对象来获取创建时间和最后一次访问时间。我们将会为request对象关联一个新的session对象,如果这个对象尚未存在的话。
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ page import="java.io.*,java.util.*" %> <% // 获取session创建时间 Date createTime = new Date(session.getCreationTime()); // 获取最后访问页面的时间 Date lastAccessTime = new Date(session.getLastAccessedTime()); String title = "再次访问菜鸟教程实例"; Integer visitCount = new Integer(0); String visitCountKey = new String("visitCount"); String userIDKey = new String("userID"); String userID = new String("ABCD"); // 检测网页是否有新的访问用户 if (session.isNew()){ title = "访问菜鸟教程实例"; session.setAttribute(userIDKey, userID); session.setAttribute(visitCountKey, visitCount); } else { visitCount = (Integer)session.getAttribute(visitCountKey); visitCount += 1; userID = (String)session.getAttribute(userIDKey); session.setAttribute(visitCountKey, visitCount); } %> <html> <head> <title>Session 跟踪</title> </head> <body> <h1>Session 跟踪</h1> <table border="1" align="center"> <tr bgcolor="#949494"> <th>Session 信息</th> <th>值</th> </tr> <tr> <td>id</td> <td><% out.print( session.getId()); %></td> </tr> <tr> <td>创建时间</td> <td><% out.print(createTime); %></td> </tr> <tr> <td>最后访问时间</td> <td><% out.print(lastAccessTime); %></td> </tr> <tr> <td>用户 ID</td> <td><% out.print(userID); %></td> </tr> <tr> <td>访问次数</td> <td><% out.print(visitCount); %></td> </tr> </table> </body> </html>试着访问 http://localhost:8080/testjsp/main.jsp ,第一次运行时将会得到如下结果:
再次访问,将会得到如下结果:
当处理完一个用户的会话数据后,您可以有如下选择:
移除一个特定的属性: 调用public void removeAttribute(String name) 方法来移除指定的属性。删除整个会话: 调用public void invalidate() 方法来使整个session无效。设置会话有效期: 调用 public void setMaxInactiveInterval(int interval) 方法来设置session超时。登出用户: 支持servlet2.4版本的服务器,可以调用 logout()方法来登出用户,并且使所有相关的session无效。配置web.xml文件: 如果使用的是Tomcat,可以向下面这样配置web.xml文件: <session-config> <session-timeout>15</session-timeout> </session-config>超时以分钟为单位,Tomcat中的默认的超时时间是30分钟。 Servlet中的getMaxInactiveInterval( ) 方法以秒为单位返回超时时间。如果在web.xml中配置的是15分钟,则getMaxInactiveInterval( ) 方法将会返回900。
当需要将文档移动到一个新的位置时,就需要使用JSP重定向了。 最简单的重定向方式就是使用response对象的sendRedirect()方法。这个方法的签名如下:
public void response.sendRedirect(String location) throws IOException这个方法将状态码和新的页面位置作为响应发回给浏览器。您也可以使用setStatus()和setHeader()方法来得到同样的效果:
.... String site = "http://www.runoob.com" ; response.setStatus(response.SC_MOVED_TEMPORARILY); response.setHeader("Location", site); ....有时候我们需要知道某个页面被访问的次数,这时我们就需要在页面上添加页面统计器,页面访问的统计一般在用户第一次载入时累加该页面的访问数上。 要实现一个计数器,您可以利用应用程序隐式对象和相关方法getAttribute()和setAttribute()来实现。 这个对象表示JSP页面的整个生命周期中。当JSP页面初始化时创建此对象,当JSP页面调用jspDestroy()时删除该对象。 以下是在应用中创建变量的语法:
application.setAttribute(String Key, Object Value);您可以使用上述方法来设置一个计数器变量及更新该变量的值。读取该变量的方法如下:
application.getAttribute(String Key);在页面每次被访问时,你可以读取计数器的当前值,并递增1,然后重新设置,在下一个用户访问时就将新的值显示在页面上。
实例演示 该实例将介绍如何使用JSP来计算特定页面访问的总人数。如果你要计算你网站使用页面的总点击量,那么你就必须将该代码放在所有的JSP页面上。
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ page import="java.io.*,java.util.*" %> <html> <html> <head> <title>访问量统计</title> </head> <body> <% Integer hitsCount = (Integer)application.getAttribute("hitCounter"); if( hitsCount ==null || hitsCount == 0 ){ /* 第一次访问 */ out.println("欢迎访问菜鸟教程!"); hitsCount = 1; }else{ /* 返回访问值 */ out.println("欢迎再次访问菜鸟教程!"); hitsCount += 1; } application.setAttribute("hitCounter", hitsCount); %> <p>页面访问量为: <%= hitsCount%></p> </body> </html>现在我们将上面的代码放置于main.jsp文件上,并访问http://localhost:8080/testjsp/main.jsp文件。你会看到页面会生成个计数器,在我们每次刷新页面时,计数器都会发生变化(每次刷新增加1)。 你也可以通过不同的浏览器访问,计数器会在每次访问后增加1。如下所示:
复位计数器 使用以上方法,在 web 服务器重启后,计数器会被复位为 0,即前面保留的数据都会消失,你可以使用以下几种方式解决该问题: 在数据库中定义一个用于统计网页访问量的数据表 count,字段为 hitcount,hitcount 默认值为0,将统计数据写入到数据表中。 在每次访问时我们读取表中 hitcount 字段。 每次访问时让 hitcount 自增 1。 在页面上显示新的 hitcount 值作为页面的访问量。 如果你需要统计每个页面的访问量,你可以使用以上逻辑将代码添加到所有页面上。