目录
过滤器概念
Filter也称之为过滤器,它是Servlet技术中最实用的技术,Web开发人员通过Filter技术,对web服务器管理的所有web资源:例如Jsp, Servlet, 静态图片文件或静态 html 文件等进行拦截,从而实现一些特殊的功能。例如实现URL级别的权限访问控制、过滤敏感词汇、压缩响应信息等一些高级功能处理编码。
它主要用于对用户请求进行预处理,也可以对HttpServletResponse进行后处理。使用Filter的完整流程:Filter对用户请求进行预处理,接着将请求交给Servlet进行处理并生成响应,最后Filter再对服务器响应进行后处理。
过滤器如何实现功能
- 在HttpServletRequest到达 Servlet 之前,拦截客户的HttpServletRequest 。根据需要检查HttpServletRequest,也可以修改HttpServletRequest 头和数据。
- 在HttpServletResponse到达客户端之前,拦截HttpServletResponse 。根据需要
- Filter接口中有一个doFilter方法,当开发人员编写好Filter,并配置对哪个web资源进行拦截后,Web服务器每次在调用web资源的service方法之前,都会先调用一下filter的doFilter方法,doFilter方法中有一个filterChain对象,用于继续传递给下一个filter,在传递之前我们可以定义过滤请求的功能,在传递之后,我们可以定义过滤响应的功能
过滤器的定义和配置
- 开发后台资源 静态资源(html,css … …)或者动态资源(Servlet,Jsp)
- 开发Filter
- 在web.xml中配置Filter拦截哪些资源
案例:定义和使用Filter
开发servlet1
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class MyServlet1 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("myServlet1 执行了sevice方法");
resp.setContentType("text/html;charset=UTF-8");
resp.setCharacterEncoding("UTF-8");
resp.getWriter().print("myServlet1响应的数据");
}
}
开发servlet2
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class MyServlet2 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("myServlet2 执行了sevice方法");
resp.setContentType("text/html;charset=UTF-8");
resp.setCharacterEncoding("UTF-8");
resp.getWriter().print("myServlet2响应的数据");
}
}
开发Filter
import javax.servlet.*;
import java.io.IOException;
public class MyFilter 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("Filter doFilter 对请求作出过滤");
// 通过一行代码 放行请求
// 放行请求,交给过滤器链继续进行过滤 最后到达资源
filterChain.doFilter(servletRequest, servletResponse);
System.out.println("Filter doFilter 对响应作出过滤");
servletResponse.getWriter().print("filter 追加一些数据");
}
// 销毁方法
@Override
public void destroy() {
}
}
web.xml中配置Filter
<?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_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>myServlet1</servlet-name>
<servlet-class>com.msb.servlet.MyServlet1</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>myServlet1</servlet-name>
<url-pattern>/myServlet1.do</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>myServlet2</servlet-name>
<servlet-class>com.msb.servlet.MyServlet2</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>myServlet2</servlet-name>
<url-pattern>/myServlet2.do</url-pattern>
</servlet-mapping>
<filter>
<filter-name>filter1</filter-name>
<filter-class>com.msb.filter.MyFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>filter1</filter-name>
<!--对那个/些资源的请求和响应进行过滤-->
<!--<url-pattern>/myServlet1.do</url-pattern>-->
<servlet-name>myServlet1</servlet-name>
<servlet-name>myServlet2</servlet-name>
<!--<url-pattern>/</url-pattern>
<url-pattern>/*</url-pattern>-->
</filter-mapping>
</web-app>
过滤器的生命周期及案例
同servlet对象一样,Filter对象的创建也是交给web服务器完成的,在web服务器创建和使用及最后销毁filter时,会调用filter对应的方法。和我们编写的Servlet程序一样,Filter的创建和销毁由WEB服务器负责。 web 应用程序启动时,web 服务器将创建Filter 的实例对象,并调用其init方法,读取web.xml配置,完成对象的初始化功能,从而为后续的用户请求作好拦截的准备工作(filter对象只会创建一次,init方法也只会执行一次)。开发人员通过init方法的参数,可获得代表当前filter配置信息的FilterConfig对象。
- WEB 容器启动时,会对Filter进行构造并初始化 一次
- 每次请求目标资源时,都会执行doFilter的方法
- WEB容器关闭是,会销毁Filter对象
构造方法: 实例化一个Filter对象的方法
初始化方法: public void init(FilterConfig filterConfig);
拦截请求方法: public void doFilter(),这个方法完成实际的过滤操作。当客户请求访问与过滤器关联的URL的时候,Servlet过滤器将先执行doFilter方法。FilterChain参数用于访问后续过滤器。
销毁方法: public void destroy(); Filter对象创建后会驻留在内存,当web应用移除或服务器停止时才销毁。在Web容器卸载 Filter 对象之前被调用。该方法在Filter的生命周期中仅执行一次。在这个方法中,可以释放过滤器使用的资源。
生命周期案例:
import javax.servlet.*;
import java.io.IOException;
public class MyFilter implements Filter {
public MyFilter(){
System.out.println("MyFilter constructor invoked");
}
// 初始化方法
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("MyFilter init invoked");
}
// 作出过滤的方法
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("Filter doFilter 对请求作出过滤");
// 通过一行代码 放行请求
// 放行请求,交给过滤器链继续进行过滤 最后到达资源
filterChain.doFilter(servletRequest, servletResponse);
System.out.println("Filter doFilter 对响应作出过滤");
servletResponse.getWriter().print("filter 追加一些数据");
}
// 销毁方法
@Override
public void destroy() {
System.out.println("MyFilter destory invoked");
}
}
过滤器链
在一个web应用中,可以开发编写多个Filter,这些Filter组合起来称之为一个Filter链。
-
web服务器根据Filter在web.xml文件中的注册顺序,决定先调用哪个Filter,当第一个Filter的doFilter方法被调用时,web服务器会创建一个代表Filter链的FilterChain对象传递给该方法。在doFilter方法中,开发人员如果调用了FilterChain对象的doFilter方法,则web服务器会检查FilterChain对象中是否还有filter,如果有,则调用第2个filter,如果没有,则调用目标资源。
-
使用过滤器链的好处是我们可以将不同的过滤功能分散到多个过滤器中,分工明确,避免一个过滤器做太多的业务处理,降低了代码的耦合度,这体现了单一职责的设计原则,应用了责任链的代码设计模式.
-
决定过滤器的执行顺序是由filter-mapping标签决定
过滤器链案例:
第一个过滤器
import javax.servlet.*;
import java.io.IOException;
import java.util.Enumeration;
public class MyFilter1 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("MyFilter1 在过滤请求 ");
filterChain.doFilter(servletRequest,servletResponse);
System.out.println("MyFilter1 在过滤响应");
}
@Override
public void destroy() {
}
}
第二个过滤器
import javax.servlet.*;
import java.io.IOException;
public class MyFilter2 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("MyFilter2 在过滤请求 ");
filterChain.doFilter(servletRequest,servletResponse);
System.out.println("MyFilter2 在过滤响应");
}
@Override
public void destroy() {
}
}
配置Filter
<?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_4_0.xsd"
version="4.0">
<filter>
<filter-name>filter1</filter-name>
<filter-class>com.msb.filter.MyFilter1</filter-class>
</filter>
<filter>
<filter-name>filter2</filter-name>
<filter-class>com.msb.filter.MyFilter2</filter-class>
</filter>
<!--这里的顺序决定了过滤器的顺序-->
<filter-mapping>
<filter-name>filter2</filter-name>
<url-pattern>/myServlet1.do</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>filter1</filter-name>
<url-pattern>/myServlet1.do</url-pattern>
</filter-mapping>
</web-app>
过滤器初始化参数:
同servlet一样,filter也可以通过web.xml进行初始化配置,在初始化时,将参数封装进入FilterConfig并在调用init方法时作为实参传入,我们可以在init方法中获取参数。FilterConfig接口为我们提供了如下功能:
String getFilterName();//得到filter的名称。
String getInitParameter(String name);//返回定名称的初始化参数的值。如果不存在返回null.
Enumeration getInitParameterNames();//返回过滤器的所有初始化参数的名字的枚举集合。
public ServletContext getServletContext();//返回Servlet上下文对象的引用。
配置Filter初始化参数
<filter>
<filter-name>filter1</filter-name>
<filter-class>com.msb.filter.MyFilter1</filter-class>
<init-param>
<param-name>realname</param-name>
<param-value>xiaoming</param-value>
</init-param>
<init-param>
<param-name>gender</param-name>
<param-value>boy</param-value>
</init-param>
<init-param>
<param-name>age</param-name>
<param-value>10</param-value>
</init-param>
<init-param>
<param-name>charset</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
读取初始化参数
public class MyFilter1 implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
// 获取初始化的一些参数
String realname = filterConfig.getInitParameter("realname");
System.out.println("realname:"+realname);
Enumeration<String> pNames = filterConfig.getInitParameterNames();
while(pNames.hasMoreElements()){
String pName = pNames.nextElement();
System.out.println(pName+":"+filterConfig.getInitParameter(pName));
}
}
过滤器注解方式开发
@WebFilter的属性属性如下图所示:
过滤器注解案例:
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.annotation.WebInitParam;
import java.io.IOException;
@WebFilter(urlPatterns = "/myServlet1.do")
public class Filter0_MyFilter 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("MyFilter0 在过滤请求 ");
filterChain.doFilter(servletRequest,servletResponse);
System.out.println("MyFilter0 在过滤响应");
}
@Override
public void destroy() {
}
}
初始化过滤器注解案例
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.annotation.WebInitParam;
import java.io.IOException;
import java.util.Enumeration;
@WebFilter(urlPatterns = "/myServlet1.do",initParams = {@WebInitParam(name="realname",value ="zhangsan"),@WebInitParam(name="charset",value ="utf-8")})
public class Filter1_MyFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
// 获取初始化的一些参数
String realname = filterConfig.getInitParameter("realname");
System.out.println("realname:"+realname);
Enumeration<String> pNames = filterConfig.getInitParameterNames();
while(pNames.hasMoreElements()){
String pName = pNames.nextElement();
System.out.println(pName+":"+filterConfig.getInitParameter(pName));
}
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("MyFilter1 在过滤请求 ");
filterChain.doFilter(servletRequest,servletResponse);
System.out.println("MyFilter1 在过滤响应");
}
@Override
public void destroy() {
}
}
实战案例:过滤器解决Post中文乱码
准备页面login.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>$Title%sSourceCode%lt;/title>
</head>
<body>
please login ... ... <br/>
<form action="loginController.do" method="post">
用户名:<input type="text" name="user"> <br/>
密码:<input type="password" name="pwd"><br/>
<input type="submit" value="提交">
</form>
</body>
</html>
准备servlet , LoginController
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet(urlPatterns = "/loginController.do")
public class LoginController extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取用户名和密码
String user = req.getParameter("user");
String pwd = req.getParameter("pwd");
System.out.println(user);
System.out.println(pwd);
}
}
准备过滤器:
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
public class Filter0_EncodingFilter implements Filter {
private String charset;
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
servletRequest.setCharacterEncoding(charset);
// 放行
filterChain.doFilter(servletRequest, servletResponse);
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
charset = filterConfig.getInitParameter("charset");
}
@Override
public void destroy() {
}
}
配置过滤器:
<?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_4_0.xsd"
version="4.0">
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>com.msb.filter.Filter0_EncodingFilter</filter-class>
<init-param>
<param-name>charset</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>*.do</url-pattern>
</filter-mapping>
</web-app>
实战案例:用户登录验证
需求: 通过过滤器控制,只有登录过之后可以反复进入welcome.jsp欢迎页,如果没有登录,提示用户进入登录页进行登录操作
实体类User
import java.io.Serializable;
public class User implements Serializable {
private String username;
private String password;
@Override
public String toString() {
return "User{" +
"username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
public User() {
}
public User(String username, String password) {
this.username = username;
this.password = password;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
准备一些页面和静态资源
login.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>$Title%sSourceCode%lt;/title>
</head>
<body>
<img src="static/img/logo.png">
please login ... ... <br/>
<form action="loginController.do" method="post">
用户名:<input type="text" name="user"> <br/>
密码:<input type="password" name="pwd"><br/>
<input type="submit" value="提交">
</form>
</body>
</html>
welcome.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<img src="static/img/logo.png">
欢迎${user.username}登陆!!!
</body>
</html>
aaa.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
this is page aaa
</body>
</html>
Controller代码
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet(urlPatterns = "/loginController.do")
public class LoginController extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取用户名和密码
String username = req.getParameter("user");
String password = req.getParameter("pwd");
System.out.println(username);
System.out.println(password);
// 链接数据库校验登录
// 登录成功,将用户信息放入Session域
User user =new User(username,password);
req.getSession().setAttribute("user", user);
// 跳转到欢迎页
resp.sendRedirect("welcome.jsp");
}
}
准备登录控制过滤器
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
@WebFilter(urlPatterns = "/*")// 任何资源都要进行过滤,
public class Filter1_LoginFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest req=(HttpServletRequest)servletRequest;
HttpServletResponse resp=(HttpServletResponse) servletResponse;
//无论是否登录过,都要放行的资源 登录页 登录校验Controller 和一些静态资源
String requestURI = req.getRequestURI();
System.out.println(requestURI);
// 当浏览器访问login.jsp时,如果login.jsp中包含了其它静态资源,比如img,js,css等,浏览器会再次访问web服务器请求资源,这些请求也会被过滤器拦截到,所以需要把这些静态资源放在/static/中进行过滤
if(requestURI.contains("login.jsp")|| requestURI.contains("loginController.do")|| requestURI.contains("/static/")){
// 直接放行
filterChain.doFilter(req,resp);
// 后续代码不再执行
return;
}
// 需要登录之后才能访问的资源,如果没登录,重定向到login.jsp上,提示用户进行登录
HttpSession session = req.getSession();
Object user = session.getAttribute("user");
if(null != user){// 如果登录过 放行
filterChain.doFilter(req,resp);
}else{// 没有登录过,跳转至登录页
resp.sendRedirect("login.jsp");
}
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void destroy() {
}
}