Java
Java Web基础

Java Web过滤器

简介:Filter译为过滤器,是JavaWeb的三大组件之一,用于在Servlet之外对Request或者Response进行修改。对于Web应用程序来说,过滤器是一个驻留在服务器端的Web组件,它可以截取客户端和服务器端之间的请求与响应信息。

1. 过滤器简介

Filter译为过滤器,是JavaWeb的三大组件之一,用于在Servlet之外对Request或者Response进行修改。对于Web应用程序来说,过滤器是一个驻留在服务器端的Web组件,它可以截取客户端和服务器端之间的请求与响应信息。由于Servlet规范是开放的,借助于公众与开源社区的力量,Servlet规范越来越科学,功能也越来越强大。2000年,Sun公司在Servlet2.3规范中添加了Filter功能,并在Servlet2.4中对Filter进行了细节上的补充。目前主流版本为Servlet2.5的Filter。

Servlet是服务器端用于处理客户端的请求与响应的,而Filter就是介于客户端与服务器端拦截客户端的请求或服务器端的响应,并对其修改或过滤。具体实现流程如下:

当客户端向服务器端发送一个请求时,如果有对应的过滤器进行拦截,过滤器可以改变请求的内容、或者重新设置请求协议的相关信息等,然后再将请求发送给服务器端的Servlet进行处理。当Servlet对客户端做出响应时,过滤器同样可以进行拦截,将响应内容进行修改或者重新设置后,再响应给客户端浏览器。在上述过程中,客户端与服务器端并不需要知道过滤器的存在。

在一个Web应用程序中,可以部署多个过滤器进行拦截,这些过滤器组成了一个过滤器链。过滤器链中的每个过滤器负责特定的操作和任务,客户端的请求在这些过滤器之间传递,直到服务器端的Servlet。具体执行流程如下:

2. 编写一个过滤器

在Servlet API中提供了一个Filter接口,实现过滤器只需要实现该接口即可。以下是Filter接口的API:

  • public void init(FilterConfig filterConfig) throws ServletException:会在Filter初始化的时候调用,Filter会跟随容器的创建而初始化。
  • public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException:在Filter拦截到相应的请求的时候调用,如果需要放行该请求,需要手动调用chain.doFilter(request, response);
  • public void destroy():会在Filter销毁的时候调用,一般来说,Filter会跟随容器的销毁而销毁。

Filter可以作用在某个指定Servlet上,也可以指定URL Pattern,从而作用在某些匹配的请求上。具体需要在web.xml文件中进行配置。

实现Servlet过滤器的具体步骤如下:

创建一个Java类,并实现Filter接口,重写该接口的方法:

  • package com.coderap.filter;
  • import javax.servlet.*;
  • import java.io.IOException;
  • public class TestServletNameFilter implements Filter {
  • @Override
  • public void init(FilterConfig filterConfig) throws ServletException {
  • System.out.println("TestServletNameFilter init executed");
  • }
  • @Override
  • public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
  • System.out.println("TestServletNameFilter doFilter executed");
  • chain.doFilter(request, response);
  • }
  • @Override
  • public void destroy() {
  • System.out.println("TestServletNameFilter destroy executed");
  • }
  • }

然后在web.xml中进行配置,这里的示例以拦截TestServlet为例:

  • <filter>
  • <filter-name>TestServletNameFilter</filter-name>
  • <filter-class>com.coderap.filter.TestServletNameFilter</filter-class>
  • </filter>
  • <filter-mapping>
  • <filter-name>TestServletNameFilter</filter-name>
  • <servlet-name>TestServlet</servlet-name>
  • </filter-mapping>

配置完成后,启动Tomcat,并且访问TestServlet,会有以下的打印:

  • TestServletNameFilter init executed
  • TestServletNameFilter doFilter executed

当关闭Tomcat时会得到TestServletNameFilter销毁方法的打印:TestServletNameFilter destroy executed

实现URL Pattern过滤器的具体步骤与Servlet过滤器类似,唯一不同的地方在于在web.xml文件中的需要针对<filter-mapping>配置<url-pattern>,这里的示例以拦截/index.jsp为例:

  • <filter>
  • <filter-name>TestUrlPatternFilter</filter-name>
  • <filter-class>com.coderap.filter.TestUrlPatternFilter</filter-class>
  • </filter>
  • <filter-mapping>
  • <filter-name>TestUrlPatternFilter</filter-name>
  • <url-pattern>/index.jsp</url-pattern>
  • </filter-mapping>

3. 过滤器生命周期

Servlet API提供的Filter接口中含有三个方法,分别为init()doFilter()destroy()方法,这三个方式就是Filter的生命周期方法。

  • Filter的构造函数:在Tomcat服务器启动时执行。在Filter的生命周期中只执行一次。
  • init(FilterConfig)方法:在Tomcat服务器启动时执行。在Filter的生命周期中只执行一次。用于Filter的初始化工作。
  • doFilter(ServletRequest, ServletResponse, FilterChain)方法:在每次拦截时执行。在Filter的生命周期中只执行多次。用于Filter的拦截处理工作。
  • destroy()方法:在Tomcat服务器关闭时执行。在Filter的生命周期中只执行一次。用于Filter的销毁工作。

4. 过滤器链

在一个Web应用程序中,可以部署多个过滤器进行拦截,这些过滤器组成了一个过滤器链。这里我们为上面的TestServlet再增加一个过滤器:

  • package com.coderap.filter;
  • import javax.servlet.*;
  • import java.io.IOException;
  • public class TestServletMutiFilter implements Filter {
  • @Override
  • public void init(FilterConfig filterConfig) throws ServletException {
  • System.out.println("TestServletMutiFilter init executed");
  • }
  • @Override
  • public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
  • System.out.println("TestServletMutiFilter doFilter executed");
  • chain.doFilter(request, response);
  • }
  • @Override
  • public void destroy() {
  • System.out.println("TestServletMutiFilter destroy executed");
  • }
  • }

另外还需要将该过滤器配置到web.xml中,也就是说,现在TestServlet有下面的两个过滤器配置:

  • <filter>
  • <filter-name>TestServletNameFilter</filter-name>
  • <filter-class>com.coderap.filter.TestServletNameFilter</filter-class>
  • </filter>
  • <filter-mapping>
  • <filter-name>TestServletNameFilter</filter-name>
  • <servlet-name>TestServlet</servlet-name>
  • </filter-mapping>
  • <filter>
  • <filter-name>TestServletMutiFilter</filter-name>
  • <filter-class>com.coderap.filter.TestServletMutiFilter</filter-class>
  • </filter>
  • <filter-mapping>
  • <filter-name>TestServletMutiFilter</filter-name>
  • <servlet-name>TestServlet</servlet-name>
  • </filter-mapping>

需要注意的是,FilterChain的doFilter()方法执行时,如果只有一个过滤器的话,执行该方法会将请求发送给服务器端的动态或静态资源,如果是过滤器链的话,只有在执行过滤器链的最后一个过滤器的FilterChain的doFilter()方法时,才会将请求发送给服务器端的动态或静态资源。如果不是在过滤器链的最后一个过滤器的FilterChain的doFilter()方法时,将请求发送给下一个过滤器进行拦截。也就是说,如果在过滤器链中间的某个过滤中调用了FilterChain的doFilter()并不会将请求发送给服务器端的动态或静态资源,而是继续将请求发送给下一个过滤器进行拦截。同时,在过滤器链中的过滤器执行的先后顺序是按照Web工程的web.xml文件配置过滤器的先后顺序被执行。

启动Tomcat并请求TestServlet,可以得到下面的打印:

  • TestServletNameFilter doFilter executed
  • TestServletMutiFilter doFilter executed

如果此时我们将TestServletMutiFilter中的chain.doFilter(request, response)该行代码去掉,即使TestServletNameFilter中也调用了chain.doFilter(request, response),对TestServlet的请求将不会正确返回。

5. FilterConfig

在过滤器接口的init()方法中提供了FilterConfig参数,通过该参数可以获取web.xml配置过滤器的参数内容,或者获取ServletContext对象等。FilterConfig API内容如下:

  • public String getFilterName():获取过滤器名称。
  • public ServletContext getServletContext():获取ServletContext。
  • public String getInitParameter(String name):根据参数名获取初始化参数。
  • public Enumeration getInitParameterNames():获取所有初始化参数的名称。

FilterConfig对象可以一些有用的信息,包括重要的ServletContext对象,以及某些初始化参数,其中初始化参数相关的内容其实是在web.xml中定义的,我们可以为TestServletNameFilter添加一些初始化参数如下:

  • <filter>
  • <filter-name>TestServletNameFilter</filter-name>
  • <filter-class>com.coderap.filter.TestServletNameFilter</filter-class>
  • <init-param>
  • <param-name>version</param-name>
  • <param-value>1.0</param-value>
  • </init-param>
  • <init-param>
  • <param-name>desc</param-name>
  • <param-value>This is a desc of TestServletNameFilter</param-value>
  • </init-param>
  • </filter>
  • <filter-mapping>
  • <filter-name>TestServletNameFilter</filter-name>
  • <servlet-name>TestServlet</servlet-name>
  • </filter-mapping>

然后尝试在TestServletNameFilter中获取这些参数:

  • @Override
  • public void init(FilterConfig filterConfig) throws ServletException {
  • System.out.println("TestServletNameFilter init executed");
  • Enumeration initParameterNames = filterConfig.getInitParameterNames();
  • StringBuilder parameterNamesInfo = new StringBuilder();
  • parameterNamesInfo.append("TestServletNameFilter initParameterNames: [");
  • while (initParameterNames.hasMoreElements()) {
  • Object parameterName = initParameterNames.nextElement();
  • parameterNamesInfo.append(parameterName);
  • parameterNamesInfo.append(", ");
  • }
  • parameterNamesInfo.delete(parameterNamesInfo.length() - 2, parameterNamesInfo.length());
  • parameterNamesInfo.append("]");
  • System.out.println(parameterNamesInfo.toString());
  • System.out.println("TestServletNameFilter version: " + filterConfig.getInitParameter("version"));
  • System.out.println("TestServletNameFilter desc: " + filterConfig.getInitParameter("desc"));
  • }

重新启动Tomcat,可以得到下面的打印信息:

  • TestServletNameFilter init executed
  • TestServletNameFilter initParameterNames: [version, desc]
  • TestServletNameFilter version: 1.0
  • TestServletNameFilter desc: This is a desc of TestServletNameFilter

注:通过FilterConfig的getInitParameter()方法获取的初始化参数是私有参数。只有当前过滤器才能获取到,而其他过滤器并不能访问。如果配置全局初始化参数,可以使用<context-param>来配置,并使用ServletContext对象获取。

6. Filter映射配置

过滤器需要配置在web.xml中才能生效。一个典型的过滤器需要配置<filter><filter-mapping>标签,例如如下:

  • <filter>
  • <filter-name>TestServletNameFilter</filter-name>
  • <filter-class>com.coderap.filter.TestServletNameFilter</filter-class>
  • <init-param>
  • <param-name>version</param-name>
  • <param-value>1.0</param-value>
  • </init-param>
  • <init-param>
  • <param-name>desc</param-name>
  • <param-value>This is a desc of TestServletNameFilter</param-value>
  • </init-param>
  • </filter>
  • <filter-mapping>
  • <filter-name>TestServletNameFilter</filter-name>
  • <servlet-name>TestServlet</servlet-name>
  • <dispatcher>FORWARD</dispatcher>
  • </filter-mapping>

<filter>配置过滤器的名称,实现类以及初始化参数。<filter-mapping>配置当前过滤器拦截的路径。<filter-mapping>中的<url-pattern>标签用于配置当前过滤器拦截的路径,配置方式与Servlet的<url-pattern>配置方式类似,共有三种方式:

  • 完全匹配。
  • 目录匹配。
  • 扩展名匹配。

如果需要拦截的是Servlet的话,有<servlet-name><url-pattern>两种方式配置拦截路径。

<dispatcher>标签配置到达Servlet的方法,有四种取值:REQUESTFORWARDINCLUDEERROR。可以同时配置多个<dispatcher>标签,如果没有配置<dispatcher>标签,默认为REQUEST。这四种取值的区别如下:

  • REQUEST:表示仅当直接请求Servlet时才生效。
  • FORWARD:表示仅当某Servlet通过FORWARD到该Servlet时才生效。
  • INCLUDE:JSP中可以通过<jsp:include>标签请求某Servlet或调用RequestDispatcher的include()方法请求某Servlet,仅这种情况下有效。
  • ERROR:JSP中可以通过<%@ page errorPage=”error.jsp”>标签指定错误处理页面,仅这种情况下有效。

<url-pattern>标签与<dispatcher>标签的关系是“且”的关系,只有满足<url-pattern>标签的条件,且满足<dispatcher>标签的条件时,当前过滤器才能生效。

7. 过滤器案例

这里将列举几个使用过滤器的常见案例,它们都不同程度地解决了某一类问题。

7.1 全站乱码问题

中文乱码问题一直都是Web应用开发的问题,想要解决整个Web应用程序的中文乱码问题,可以进行如下几步操作:

  1. 创建一个Java类继承于HttpServletRequestWrapper类,用于重写HttpServletRequest,解决GET方式的中文乱码问题:
  • package com.coderap.filter.example.coding;
  • import javax.servlet.http.HttpServletRequest;
  • import javax.servlet.http.HttpServletRequestWrapper;
  • import java.io.UnsupportedEncodingException;
  • public class CustomizedHttpSeervletRequest extends HttpServletRequestWrapper {
  • public CustomizedHttpSeervletRequest(HttpServletRequest httpServletRequest) {
  • super(httpServletRequest);
  • }
  • @Override
  • public String getParameter(String name) {
  • String value = super.getParameter(name);
  • if (getMethod().equalsIgnoreCase("GET")) {
  • try {
  • value = new String(value.getBytes("ISO-8859-1"), "utf-8");
  • } catch (UnsupportedEncodingException e) {
  • e.printStackTrace();
  • }
  • }
  • return value;
  • }
  • }
  1. 创建一个过滤器用于解决整个Web应用程序的中文乱码问题:
  • package com.coderap.filter.example.coding;
  • import javax.servlet.*;
  • import javax.servlet.http.HttpServletRequest;
  • import java.io.IOException;
  • public class EncodingFilter implements Filter {
  • public void init(FilterConfig filterConfig) throws ServletException {
  • }
  • public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
  • request.setCharacterEncoding("utf-8");
  • response.setContentType("text/html;charset=utf-8");
  • CustomizedHttpSeervletRequest req = new CustomizedHttpSeervletRequest((HttpServletRequest) request);
  • chain.doFilter(req, response);
  • }
  • public void destroy() {
  • }
  • }

同时需要将EncodingFilter过滤器配置到web.xml文件中:

  • <filter>
  • <filter-name>EncodingFilter</filter-name>
  • <filter-class>com.coderap.filter.example.coding.EncodingFilter</filter-class>
  • </filter>
  • <filter-mapping>
  • <filter-name>EncodingFilter</filter-name>
  • <url-pattern>/*</url-pattern>
  • </filter-mapping>
  • <filter-mapping>
  • <filter-name>EncodingFilter</filter-name>
  • <servlet-name>*</servlet-name>
  • </filter-mapping>

上面的配置将对所有请求和Servlet都进行过滤操作。

  1. 使JSP页面正确显示中文,如下测试页面:
  • <%@ page contentType="text/html;charset=UTF-8" language="java" %>
  • <html>
  • <head>
  • <title>Encoding Test</title>
  • </head>
  • <body>
  • 这里是一段中文字符
  • </body>
  • </html>

使JSP页面正确显示中文有两种方式:

  • 直接在JSP页面中添加<%@ page contentType="text/html;charset=UTF-8" language="java" %>代码声明,不使用过滤器也能使JSP页面正确显示中文。
  • 配合上面编写的EncodingFilter过滤器,还需要在页面中添加<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>才能正确显示中文。
  1. 使Servlet返回内容正确显示中文,如下测试Servlet关键代码:
  • @Override
  • protected void doGet(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws ServletException, IOException {
  • Connection connection = null;
  • Statement statement = null;
  • ResultSet resultSet = null;
  • StringBuilder stringBuilder = new StringBuilder();
  • try {
  • connection = JNDIUtil.getConnection();
  • String sql = "select name, gender, email from users;";
  • statement = connection.createStatement();
  • resultSet = statement.executeQuery(sql);
  • stringBuilder.append("<p>这是一段中文字符</p>");
  • stringBuilder.append("<table border=1><tbody>");
  • stringBuilder.append("<tr>");
  • stringBuilder.append("<th>name</th>");
  • stringBuilder.append("<th>gender</th>");
  • stringBuilder.append("<th>email</th>");
  • stringBuilder.append("</tr>");
  • while (resultSet.next()) {
  • String name = resultSet.getString(1);
  • String gender = resultSet.getString(2);
  • String email = resultSet.getString(3);
  • stringBuilder.append("<tr>");
  • stringBuilder.append("<td>" + name + "</td>");
  • stringBuilder.append("<td>" + gender + "</td>");
  • stringBuilder.append("<td>" + email + "</td>");
  • stringBuilder.append("</tr>");
  • }
  • stringBuilder.append("</tbody></table>");
  • } catch (SQLException e) {
  • e.printStackTrace();
  • } finally {
  • JNDIUtil.release(connection, statement, resultSet);
  • }
  • httpServletResponse.getWriter().print(stringBuilder.toString());
  • }

使Servlet页面正确显示中文有必须要使用上面的EncodingFilter过滤器,另外必须使用Writer进行对外数据写出,如上述代码中的:

  • httpServletResponse.getWriter().print(stringBuilder.toString());

如果使用httpServletResponse.getOutputStream().print(stringBuilder.toString());则无法正确写出中文。

7.2 自动登录功能

很多网站有自动登录的功能,当用户登录成功后,在某一段时间内再次打开网站将直接自动登录,自动登录的功能也可以使用Filter完成。这里使用之前的users表来进行登录测试;向用户表中增加一个password字段列,并且给添加该列的值:

  • mysql> alter table users add column password varchar(20);
  • Query OK, 0 rows affected (0.46 sec)
  • Records: 0 Duplicates: 0 Warnings: 0
  • mysql> desc users;
  • +----------+-------------+------+-----+---------+----------------+
  • | Field | Type | Null | Key | Default | Extra |
  • +----------+-------------+------+-----+---------+----------------+
  • | id | int(11) | NO | PRI | NULL | auto_increment |
  • | name | varchar(40) | YES | | NULL | |
  • | gender | varchar(10) | YES | | NULL | |
  • | email | varchar(60) | YES | | NULL | |
  • | birthday | date | YES | | NULL | |
  • | password | varchar(20) | YES | | NULL | |
  • +----------+-------------+------+-----+---------+----------------+
  • 6 rows in set (0.03 sec)
  • mysql> update users set password = '123456' where id = 1;
  • Query OK, 1 row affected (0.04 sec)
  • Rows matched: 1 Changed: 1 Warnings: 0
  • ...
  • mysql> select * from users;
  • +----+-------+--------+-------------------+------------+----------+
  • | id | name | gender | email | birthday | password |
  • +----+-------+--------+-------------------+------------+----------+
  • | 1 | Tom | Male | Tom@coderap.com | 1993-12-04 | 123456 |
  • | 2 | Jerry | Male | Jerry@coderap.com | 1991-12-04 | 123123 |
  • | 3 | Marry | Female | Marry@coderap.com | 1999-12-04 | 456456 |
  • | 4 | Tomas | Male | Tomas@coderap.com | 2018-07-23 | 321321 |
  • | 6 | Frank | Female | Frank@coderap.com | 2018-07-27 | 654321 |
  • +----+-------+--------+-------------------+------------+----------+
  • 5 rows in set (0.00 sec)
  1. 首先编写一个登录页面login.jsp,只需要输入用户名和密码即可登录:
  • <%@ page contentType="text/html;charset=UTF-8" language="java" %>
  • <html>
  • <head>
  • <title>Login Page</title>
  • </head>
  • <body>
  • <p style="color:red;">${message}</p>
  • <form action="/doLogin" method="post">
  • <label for="username">用户名:</label>
  • <input id="username" type="text" name="username" />
  • <br />
  • <label for="password">密码:</label>
  • <input id="password" type="password" name="password" />
  • <button type="submit">登录</button>
  • </form>
  • </body>
  • </html>
  1. 我们使用之前在DBUtils中测试使用到的Person类作为POJO对象来装载从数据库查询到的数据,这里为它添加了一个password字段和相应的getter和setter方法:
  • public class User {
  • private int id;
  • private String name;
  • private String password;
  • private String gender;
  • private String email;
  • private Date birthday;
  • ...
  • }
  1. 另外简单封装了一个UserDao类进行数据库的操作:
  • public class UserDao {
  • public static User findUser(String username, String password) {
  • QueryRunner queryRunner = new QueryRunner(HikariUtil.getDataSource());
  • String sql = "select * from users where `name` = ? and `password` = ?";
  • try {
  • User user = queryRunner.query(sql, new BeanHandler<User>(User.class), username, password);
  • System.out.println(user);
  • return user;
  • } catch (Exception e) {
  • e.printStackTrace();
  • }
  • return null;
  • }
  • }
  1. 同时创建一个LoginServlet处理登录逻辑,主要代码如下:
  • @Override
  • protected void doGet(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws ServletException, IOException {
  • String username = httpServletRequest.getParameter("username");
  • String password = httpServletRequest.getParameter("password");
  • System.out.println("username: " + username + ", password: " + password);
  • User user = UserDao.findUser(username, password);
  • if (user != null) {
  • httpServletRequest.getSession().setAttribute("user", user);
  • Cookie cookie = new Cookie("autologin", username + "#" + password);
  • cookie.setMaxAge(60 * 60 * 24 * 7);
  • httpServletResponse.addCookie(cookie);
  • httpServletResponse.sendRedirect("index.jsp");
  • return;
  • } else {
  • httpServletRequest.setAttribute("message", "用户名或密码错误,请重新输入.");
  • httpServletRequest.getRequestDispatcher("login.jsp").forward(httpServletRequest, httpServletResponse);
  • return;
  • }
  • }

上面的代码主要处理登录的业务,用户在表单输入用户名和密码点击的登录之后,会请求这个Servlet,在Servlet中会取到用户名和密码,使用UserDao去数据库中查询是否存在User,如果存在就把查询到的User存入Session,然后将用户名和密码存入Cookie(正式环境Cookie的存储是需要加密的),然后重定向到index.jsp页面;而如果没有查找到用户则直接转发到login.jsp页面。相应的web.xml配置和index.jsp页面如下:

  • <servlet>
  • <servlet-name>LoginServlet</servlet-name>
  • <servlet-class>com.coderap.filter.example.login.LoginServlet</servlet-class>
  • </servlet>
  • <servlet-mapping>
  • <servlet-name>LoginServlet</servlet-name>
  • <url-pattern>/doLogin</url-pattern>
  • </servlet-mapping>
  • <%@ page contentType="text/html;charset=UTF-8" language="java" %>
  • <%@ page import="com.coderap.dbutils.User" %>
  • <html>
  • <head>
  • <title>Index Page</title>
  • </head>
  • <body>
  • <%
  • User user = (User) pageContext.getSession().getAttribute("user");
  • if (user != null) {
  • out.print("Hello: " + user.getName());
  • }
  • %>
  • </body>
  • </html>
  1. 接下来需要一个过滤器处理自动登录的功能,主要代码如下:
  • package com.coderap.filter.example.login;
  • import com.coderap.dbutils.User;
  • import javax.servlet.*;
  • import javax.servlet.http.Cookie;
  • import javax.servlet.http.HttpServletRequest;
  • import java.io.IOException;
  • public class AutoLoginFilter implements Filter {
  • @Override
  • public void init(FilterConfig filterConfig) throws ServletException {
  • }
  • @Override
  • public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
  • HttpServletRequest httpServletRequest = (HttpServletRequest) request;
  • if (httpServletRequest.getSession().getAttribute("user") != null) {
  • chain.doFilter(request, response);
  • return;
  • } else {
  • // 尝试从Cookie中获取登录信息进行自动登录
  • Cookie[] cookies = httpServletRequest.getCookies();
  • Cookie cookie = this.findCookie(cookies, "autologin");
  • if (cookie != null) {
  • String loginInfo = cookie.getValue();
  • String username = loginInfo.split("#")[0];
  • String password = loginInfo.split("#")[1];
  • User user = UserDao.findUser(username, password);
  • if (user != null) {
  • httpServletRequest.getSession().setAttribute("user", user);
  • chain.doFilter(request, response);
  • return;
  • }
  • }
  • }
  • // 如果自动登录不成功,转发到login.jsp页面
  • httpServletRequest.getRequestDispatcher("/login.jsp").forward(request, response);
  • }
  • private Cookie findCookie(Cookie[] cookies, String name) {
  • if (cookies == null) {
  • return null;
  • } else {
  • for (Cookie cookie : cookies) {
  • if (cookie.getName().equals(name)) {
  • return cookie;
  • }
  • }
  • return null;
  • }
  • }
  • @Override
  • public void destroy() {
  • }
  • }

该过滤器首先判断Session中是否有用户信息,如果有就直接放行,否则尝试从Cookie中取出之前存入的用户信息进行查找用户操作,查找到了相应的用户就放行;如果之前两步都无法满足放行条件,最后将转发请求到login.jsp页面。

对于该过滤器在web.xml中的配置如下:

  • <filter>
  • <filter-name>AutoLoginFilter</filter-name>
  • <filter-class>com.coderap.filter.example.login.AutoLoginFilter</filter-class>
  • </filter>
  • <filter-mapping>
  • <filter-name>AutoLoginFilter</filter-name>
  • <url-pattern>/index.jsp</url-pattern>
  • </filter-mapping>

该过滤器只对index.jsp页面生效。

7.3. 禁用缓存

通过在响应头中添加下面的字段值,就可以禁用浏览器缓存了:

  • Expires: -1
  • Cache-Control: no-cache
  • Pragma: no-cahce

响应头信息的添加需要在Servlet中进行:

  • httpServletResponse.setHeader("Expires", "-1");
  • httpServletResponse.setHeader("Cache-Control", "no-cache");
  • httpServletResponse.setHeader("Pragma", "no-cahce");

但这种方式只适用于某一个JSP页面或Servlet,如果需要对所有的JSP和Servlet都生效的话,可以使用Filter的方式实现,定义下面的Filter:

  • package com.coderap.filter.example.customheader;
  • import javax.servlet.*;
  • import javax.servlet.http.HttpServletResponse;
  • import java.io.IOException;
  • public class CustomHeaderFilter implements Filter {
  • @Override
  • public void init(FilterConfig filterConfig) throws ServletException {
  • }
  • @Override
  • public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
  • HttpServletResponse httpServletResponse = (HttpServletResponse) response;
  • httpServletResponse.setHeader("Expires", "-1");
  • httpServletResponse.setHeader("Cache-Control", "no-cache");
  • httpServletResponse.setHeader("Pragma", "no-cahce");
  • chain.doFilter(request, response);
  • }
  • @Override
  • public void destroy() {
  • }
  • }

然后配置Web工程中的web.xml文件即可:

  • <filter>
  • <filter-name>CustomHeaderFilter</filter-name>
  • <filter-class>com.coderap.filter.example.customheader.CustomHeaderFilter</filter-class>
  • </filter>
  • <filter-mapping>
  • <filter-name>CustomHeaderFilter</filter-name>
  • <servlet-name>*</servlet-name>
  • </filter-mapping>
  • <filter-mapping>
  • <filter-name>CustomHeaderFilter</filter-name>
  • <url-pattern>/*</url-pattern>
  • </filter-mapping>