Filter
概述
过滤器对象,可以对资源( Servlet 或静态内容)的请求或资源的响应执行过滤任务。过滤器在 doFilter 方法中执行过滤。每个过滤器都可以访问一个 FilterConfig 对象,从中获取初始化参数,一个对 ServletContext 的引用,它可以使用这个引用来加载过滤任务所需的资源。
【注意】当访问服务器的资源时,过滤器可以将请求拦截下来,并根据条件完成一些过滤的操作。
作用
过滤器用于完成一些通用的操作【封装】
1、登录验证
2、编码集处理
3、敏感字符过滤【代理】
4、日志和审核
5、数据转换和压缩
6、加密等…
常用 API
// 过滤器接口,内置三个抽象方法
interface javax.servlet.Filter
// 初始化方法
void init(FilterConfig filterConfig);
// 【重点】核心方法,过滤器的入口,用来处理过滤逻辑,可通过过滤器链对象的doFilter方法进行放行
void doFilter(ServletRequest request, ServletResponse response, FilterChain chain);
// 销毁方法
void destroy(ServletRequest request, ServletResponse response, FilterChain chain);
// 过滤器配置对象,在初始化期间将信息传递给过滤器
interface FilterConfig
// 【重点】过滤器链对象,用来调用下一个过滤器或者访问资源
interface FilterChain
【注意】每次由于浏览器请求链末端的资源而通过链传递请求/响应对象时,容器都会调用过滤器的 doFilter 方法。传入此方法的过滤器链允许过滤器将请求和响应传递给链中的下一个实体
初识
步骤
1、声明一个类实现Filter接口(javax.servlet)
2、重写三个方法
3、声明@WebFilter注解,并添加访问路径:/*
案例代码
// 声明过滤器注解
@WebFilter("/*")
public class FilterTest1 implements Filter {
/**
* 初始化方法,创建对象后马上被调用,用来对Filter做一些初始化的操作
*
* @param filterConfig 过滤器配置对象
* @throws ServletException Servlet异常
*/
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("init!!!");
}
/**
* 核心过滤方法,用来完成过滤器主要功能的方法,每次访问目标资源时都会调用。
*
* @param servletRequest 请求对象
* @param servletResponse 响应对象
* @param filterChain 过滤器链对象,调用doFilter方法进行放行
* @throws IOException IO异常
* @throws ServletException Servlet异常
*/
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("doFilter");
// 放行
filterChain.doFilter(servletRequest, servletResponse);
System.out.println("after doFilter");
}
/**
* 服务器停止时调用,用来释放资源。
*/
@Override
public void destroy() {
System.out.println("destroy");
}
}
【注意】
1、实现 Filter 接口时要注意导包
javax.servlet.Filter
2、此注解使用通配符进行URL匹配,用来对整个项目的所有路径进行过滤
@WebFilter("/*")
执行流程【重点】
Filter 不是一个 Servlet,它不能产生一个 Response,但它能够在一个Request 请求对象到达 Servlet 之前进行请求预处理,也可以在 Response 响应对象离开 Servlet 时处理响应
用户浏览器 ==> Filter1 ==> Filter2 ==> HTML/JSP ==> Servlet ==> HTML/JSP ==> Filter2 ==> Filter1 ==> 用户浏览器
生命周期
1、构造方法创建对象
2、init 方法进行初始化
3、doFilter 核心方法进行过滤
4、destory 方法进行销毁
【注意】过滤器会随着服务器的启动直接加载
案例代码
package com.fc.filter.b_life;
import javax.servlet.*;
import java.io.IOException;
/**
* Filter生命周期
*/
public class FilterLife implements Filter {
// 构造方法
public FilterLife() {
System.out.println("Filter构造方法");
}
// 初始化方法
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("Filter初始化方法");
}
// 核心过滤方法
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("Filter核心过滤方法");
// 放行
filterChain.doFilter(servletRequest, servletResponse);
System.out.println("放行");
}
// 销毁方法
@Override
public void destroy() {
System.out.println("Filter销毁方法");
}
}
两种配置方式【重点】
注解
// 过滤器注解,默认属性为访问路径
@WebFilter("/*")
public class FilterTest1 implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("执行 Filter 初始化方法");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
filterChain.doFilter(servletRequest, servletResponse);
System.out.println("执行 Filter 过滤方法");
}
@Override
public void destroy() {
System.out.println("执行 Filter 销毁方法");
}
}
通过 web.xml 文件
案例代码
/**
* 通过web.xml文件配置过滤器
*/
public class FilterTest2 implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("初始化");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("过滤");
// 放行
filterChain.doFilter(servletRequest, servletResponse);
System.out.println("放行");
}
@Override
public void destroy() {
System.out.println("销毁");
}
}
web.xml 文件
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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>FilterTest2</filter-name>
<!--完整的包名.类名-->
<filter-class>com.fc.filter.a_create.FilterTest2</filter-class>
</filter>
<!--过滤器映射-->
<filter-mapping>
<!--过滤器名-->
<filter-name>FilterTest2</filter-name>
<!--访问路径-->
<url-pattern>/*</url-pattern>
</filter-mapping>
<!--过滤器第二个映射-->
<filter-mapping>
<!--过滤器名-->
<filter-name>FilterTest2</filter-name>
<!--访问路径-->
<url-pattern>/index.jsp</url-pattern>
</filter-mapping>
</web-app>
【注意】
- 关注的重点是需要过滤的资源,即哪些资源需要进行过滤操作
- 允许多个 Filter 同时过滤一个路径,例如机场需要进行多次安检
常用配置项
属性 | 描述 |
---|---|
urlPatterns | 配置要拦截的资源路径 |
initParams | 过滤器配置初始化参数,和 Servlet 中一样 |
dispatcherTypes | 配置拦截的类型,可配置多个,默认为DispatcherType.REQUEST |
【注意】拦截路径和URL匹配原则相同
1、以指定资源匹配,例如"/index.jsp"
2、以目录匹配,例如"/servlet/"
3、以后缀名匹配,例如".jsp"
4、通配符,拦截所有web资源。“/*”
DispatcherType 是个数组,可以过滤有多种过滤类型,包含以下值
过滤类型 | 描述 |
---|---|
REQUEST | 默认值。浏览器直接请求资源 |
FORWARD | 过滤转发访问资源 |
INCLUDE | 过滤包含访问资源 |
ERROR | 过滤错误资源 |
ASYNC | 过滤异步访问资源 |
【注意】通过 web.xml 中的 dispatcher 标签也可以配置
<dispatcher></dispatcher>
过滤路径
// 过滤以 .do 结尾的路径
// @WebFilter("*.do")
// 过滤 c_urlfilter 文件下所有
// @WebFilter("/c_urlfilter/*")
// 过滤指定文件
@WebFilter("/TestServlet.do")
public class UrlFilter implements Filter {
public void destroy() {
}
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException,
IOException {
//chain.doFilter(req, resp);
System.out.println("执行 doFilter 方法");
}
public void init(FilterConfig config) throws ServletException {
}
}
过滤转发
过滤器
/**
* 过滤类型为转发
*/
@WebFilter(dispatcherTypes = DispatcherType.FORWARD, urlPatterns = {"/*"})
public class FilterConfig2 implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("FilterConfig执行转发");
// filterChain.doFilter(servletRequest, servletResponse);
}
@Override
public void destroy() {
}
}
转发 Servlet
@WebServlet("/forward")
public class ForwardServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("测试转发");
// 放行,因为是重定向,URL 改变
// resp.sendRedirect("http://www.baidu.com");
// 过滤,因为是转发
req.getRequestDispatcher("index.jsp").forward(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
【注意】Filter 过滤的是 URL,重定向的 URL 会一直改变,所以 DispatcherType.FORWORD 无法过滤重定向
过滤器链
请求到达 Servlet 之间是可以经过多个过滤器的
【注意】过滤器执行顺序
1、在 web.xml 中,Filter 执行顺序跟 的顺序有关,先声明的先执行
2、使用注解配置的话,Filter 的执行顺序跟名称的字母顺序有关,例如 AFilter 会比 BFilter 先执行
3、如果既有在 web.xml 中声明的 Filter,也有通过注解配置的Filter,那么会优先执行 web.xml 中配置的 Filter
过滤器一
public class FilterA implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("FilterA过滤前");
filterChain.doFilter(servletRequest, servletResponse);
System.out.println("FilterA过滤后");
}
@Override
public void destroy() {
}
}
过滤器二
public class FilterB implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("FilterB过滤前");
filterChain.doFilter(servletRequest, servletResponse);
System.out.println("FilterB过滤后");
}
@Override
public void destroy() {
}
}
过滤器三
public class FilterC implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("FilterC过滤前");
filterChain.doFilter(servletRequest, servletResponse);
System.out.println("FilterC过滤后");
}
@Override
public void destroy() {
}
}
web.xml 配置文件
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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>FilterA</filter-name>
<filter-class>com.fc.filter.d_filterchain.FilterA</filter-class>
</filter>
<filter>
<filter-name>FilterB</filter-name>
<filter-class>com.fc.filter.d_filterchain.FilterB</filter-class>
</filter>
<filter>
<filter-name>FilterC</filter-name>
<filter-class>com.fc.filter.d_filterchain.FilterC</filter-class>
</filter>
<filter-mapping>
<filter-name>FilterC</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>FilterB</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>FilterA</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
【注意】DispatcherType.REQUEST 比 DispatcherType.FORWORD 优先级高,又因为默认带有 REQUEST,所以 /* 的优先级是最高的
字符集编码过滤器【重点】
/**
* 请求响应字符编码集过滤器
*/
@WebFilter(urlPatterns = {"/*"},
initParams = {@WebInitParam(name = "characterEncoding", value = "UTF8"),
@WebInitParam(name = "contextType", value = "text/html; charset=UTF-8")})
public class EncodingFilter implements Filter {
// 声明编码集和文本内容
private String characterEncoding;
private String contextType;
@Override
public void init(FilterConfig filterConfig) throws ServletException {
// 通过过滤器配置对象中的初始化参数获取编码集和文本内容
characterEncoding = filterConfig.getInitParameter("characterEncoding");
contextType = filterConfig.getInitParameter("contextType");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
// 将请求和响应对象强转为支持HTTP协议的请求和响应对象
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
// 设置请求和响应对象的编码集
request.setCharacterEncoding(characterEncoding);
response.setContentType(contextType);
// 放行
filterChain.doFilter(servletRequest, servletResponse);
}
@Override
public void destroy() {
}
}
【注意】后面直接复制粘贴即可
自动登录过滤器【重点】
登录页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登录页面</title>
</head>
<body>
<form method="post" action="login">
<table align="center">
<tr>
<td>账号</td>
<td><input type="text" name="username"></td>
</tr>
<tr>
<td>密码</td>
<td><input type="password" name="password"></td>
</tr>
<tr>
<td colspan="2" align="center">
<input type="reset" value="重置">
<input type="submit" value="登录">
</td>
</tr>
</table>
</form>
</body>
</html>
主页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>主页</title>
</head>
<body>
<h1 align="right" style="color: blue"><a href="logout">退出登录</a></h1>
<h1 align="center" style="color: aqua">欢迎${user.nickname}登录成功!!</h1>
</body>
</html>
登录失败页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登录失败页</title>
</head>
<body>
<h1 align="center" style="color: deeppink"><a href="login.html">重新登录</a></h1>
</body>
</html>
登录业务逻辑
/**
* 登录
*/
@WebServlet("/login")
public class LoginServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取参数
String username = req.getParameter("username");
String password = req.getParameter("password");
// 获取核心类对象
QueryRunner queryRunner = new QueryRunner();
// 获取数据库连接
Connection connection = JdbcUtilsOnC3P0.getConnection();
// 准备SQL语句
String sql = "select * from user where username = ? and password = ?";
// 准备参数
Object[] params = {username, password};
// 提取实体类对象
User user = null;
try {
// 执行SQL语句并获取实体类对象
user = queryRunner.query(connection, sql, new BeanHandler<>(User.class), params);
} catch (SQLException e) {
e.printStackTrace();
}
// 如果实体类对象不为null,说明查询到数据
if (user != null) {
// 登录成功,创建session
HttpSession session = req.getSession(true);
// 将实体类对象作为值添加到session中
session.setAttribute("user", user);
// 设置session过期时间
session.setMaxInactiveInterval(60 * 60);
// 获取session的id
String id = session.getId();
// 使用JSESSIONID和session的id作为键值对创建cookie对象
Cookie cookie = new Cookie("JSESSIONID", id);
// 设置cookie过期时间
cookie.setMaxAge(60 * 60);
// 将cookie发送到浏览器
resp.addCookie(cookie);
// 转发转至主页
req.getRequestDispatcher("index.jsp").forward(req, resp);
} else {
// 登录失败,重定向至登录失败页面
resp.sendRedirect("loginFail.html");
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
退出登录业务逻辑
/**
* 退出登录
*/
@WebServlet("/logout")
public class LogoutServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取session,参数要使用false
HttpSession session = req.getSession(false);
// 判断session是否为空
if (session != null) {
// session不为空,销毁session
session.invalidate();
// 根据JSESSIONID获取cookie
Cookie cookie = new Cookie("JSESSIONID", session.getId());
// 设置cookie过期时间为0
cookie.setMaxAge(0);
// 将cookie发送到浏览器
resp.addCookie(cookie);
}
// 跳转至登录页面
resp.sendRedirect("login.html");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
自动登录过滤器
/**
* 自动登录过滤器
*
* 如果没有登录则自动跳到登录页面
*/
@WebFilter("/*")
public class AutoLoginFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
// 将请求和响应对象强转为支持HTTP协议的对象
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
// 通过请求对象获取session对象,注意参数要用false
HttpSession session = request.getSession(false);
// 获取请求URI
String uri = request.getRequestURI();
// 判断请求URI是否以指定登录路径结尾
if (uri.endsWith("/login.html") || uri.endsWith("/login")) {
// 放行
filterChain.doFilter(request, response);
// 如果session为空,说明没有登录
} else if (session == null) {
// 跳转至登录页面
response.sendRedirect("login.html");
// session不为空,并且user参数也不为空
} else if (session.getAttribute("user") != null) {
// 放行
filterChain.doFilter(request, response);
} else {
// 跳转至登录页面
response.sendRedirect("login.html");
}
}
@Override
public void destroy() {
}
}
【注意】
1、如果没有设置字符集编码过滤器就需要手动设置
RequestURI();
// 判断请求URI是否以指定登录路径结尾
if (uri.endsWith("/login.html") || uri.endsWith("/login")) {
// 放行
filterChain.doFilter(request, response);
// 如果session为空,说明没有登录
} else if (session == null) {
// 跳转至登录页面
response.sendRedirect("login.html");
// session不为空,并且user参数也不为空
} else if (session.getAttribute("user") != null) {
// 放行
filterChain.doFilter(request, response);
} else {
// 跳转至登录页面
response.sendRedirect("login.html");
}
}
@Override
public void destroy() {
}
}
>【注意】
>
> 1、如果没有设置字符集编码过滤器就需要手动设置
>
> 2、自动登录过滤器以后用到了直接复制粘贴,并且更改 session 中的属性键名即可