Filter过滤器工作原理和应用详解
1. Filter 过滤器原理
1.1 Filter 基本信息
Filter
,过滤器接口
。
对客户端向服务器发送的请求进行过滤,用于在请求之前处理资源的组件。
Filter和Listener都属于Servlet中的高级部分,Filter是最为实用的技术
。
1.2 Filter 过滤器链
请求时,从客户端到服务端顺序处理;
响应时,从服务端到客户端顺序处理。
遵从原则:先过滤,后放行。
1.3 Filter 工作原理
执行流程:
- 浏览器发起请求
- 服务器会根据这个请求,创建 request 对象及 response 对象
- 过滤器会持有 request 对象及 response 对象
- 只有当过滤器放行之后,request 对象及 response 对象才会传给 Servlet
1.4 Filter 生命周期
- 随着服务器的初始化而初始化
- 随着服务器的关闭而销毁
1.5 Filter 基本使用
开发步骤:
- 自定义类
实现 Filter 接口
- 重写
init
()doFilter
()destroy()
三个方法 - 在 web.xml 中配置过滤器:①声明过滤器 ②配置过滤器的过滤路径
创建一个过滤器:
public class Filter01 implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("Filter01 过滤器的初始化");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("Filter01 放行之前");
// 通过过滤器链操作:放行
filterChain.doFilter(servletRequest, servletResponse);
System.out.println("Filter01 放行之后");
}
@Override
public void destroy() {
System.out.println("Filter01 过滤器的销毁");
}
}
配置过滤器,使其在服务器中指定资源下生效:
<!--声明Filter01过滤器-->
<filter>
<filter-name>Filter01</filter-name>
<filter-class>com.demo.filter.Filter01</filter-class>
</filter>
<!--配置Filter01的过滤路径-->
<filter-mapping>
<filter-name>Filter01</filter-name>
<!--/* 代表所有资源都过滤-->
<url-pattern>/*</url-pattern>
</filter-mapping>
1.6 Filter 配置方式 × 2
- web.xml
<url-pattern>
标签中的匹配规则:/
完全匹配/开头,*结尾
目录匹配*开头,后缀名结尾
后缀名匹配
<!--无限接近SpringMVC中文乱码解决方案-->
<filter>
<filter-name>EncodingFilter</filter-name>
<filter-class>com.demo.filter.EncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>EncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
- 注解配置
@WebFilter注解
WebInitParam[] initParams() default {}; //配置初始化参数
String filterName() default “”; //配置过滤器名称
String[] servletNames() default {}; //配置过滤的Servlet
String[] urlPatterns() default {}; //配置过滤路径
@WebFilter(filterName = "Demo04Filter" ,
urlPatterns = "/demo01",
servletNames = "Demo01Servlet" ,
initParams = {
@WebInitParam(name = "username",value = "root"),
@WebInitParam(name = "password",value = "root123")
})
public class Demo04Filter implements Filter { }
1.7 Filter 中文乱码处理
- 方式一:使用默认 .jsp 动态页面,中文乱码
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>登陆</title>
</head>
<body>
<form action="/demo/login" method="post">
账户:<input type="text" name="username"/><br>
密码:<input type="text" name="password"/><br>
7天内自动登录:<input type="checkbox" name="autoLogin" value="logined"><br>
<button type="submit">登录</button>
</form>
</body>
</html>
- 方式二:使用过滤器对 request 中的服务器编码/客户端解码进行设置
(服务器初始化时会从web.xml中读取出filter的配置信息,在任何请求过程中执行Servlet前,/* 即所有的项目资源均会先被该 Filter 过滤,设置编码方式为utf-8)
<!--web.xml-->
<filter>
<filter-name>EncodingFilter</filter-name>
<filter-class>com.demo.filter.EncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>EncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
public class EncodingFilter implements Filter {
private String encoding = null;
@Override
public void init(FilterConfig config) throws ServletException {
encoding = config.getInitParameter("encoding");
}
@Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
// 处理响应中文乱码
//resp.setContentType("text/html;charset=" + encoding);
// 处理请求中文乱码
req.setCharacterEncoding(encoding);
// 放行
chain.doFilter(req, resp);
}
@Override
public void destroy() { }
}
2. Filter 过滤器应用
2.1 案例:Filter 过滤器实现自动登陆(流程图+核心实现)
- 逻辑流程:
- 逻辑流程图梳理(单击放大更易查看):
login.jsp
<form action="/demo/login" method="post">
账户:<input type="text" name="username"/><br>
密码:<input type="text" name="password"/><br>
7天内自动登录:<input type="checkbox" name="autoLogin" value="logined"><br>
<button type="submit">登录</button>
</form>
AutoLoginServlet.java
@WebServlet(name = "AutoLoginServlet", urlPatterns = "/login")
public class AutoLoginServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String username = request.getParameter("username");
String password = request.getParameter("password");
if ("root".equals(username) && "1234".equals(password)) {
String autoLogin = request.getParameter("autoLogin");
System.out.println(autoLogin);
if ("logined".equals(autoLogin)) {
// 进行自动登陆,将用户信息保存起来,cookie最合适
Cookie cookie = new Cookie("autoLogin", username + "-" + password);
cookie.setMaxAge(7*24*60*60);
response.addCookie(cookie);
}
// 登陆成功,转发到一个页面,显示用户信息
Userinfo userinfo = new Userinfo();
userinfo.setUsername(username);
userinfo.setPassword(password);
request.getSession().setAttribute("existUser", userinfo);
request.getRequestDispatcher("/showIndex").forward(request, response);
} else {
// 登陆失败,转发到登陆页面
request.getRequestDispatcher("/login.jsp").forward(request, response);
}
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
AutoLoginFilter.java
public class AutoLoginFilter implements Filter {
@Override
public void destroy() {
}
@Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
HttpServletRequest request = (HttpServletRequest) req;
// 获取请求路径
String requestURI = request.getRequestURI();
System.out.println("0 requestURI:" + requestURI);
// 1.判断访问资源是否和登陆相关
if (requestURI.contains("login")) {
// 登陆相关资源,直接放行
chain.doFilter(req, resp);
} else {
// 非登陆相关资源
// 2.判断是否在登陆状态 Session
Userinfo existUser = (Userinfo) request.getSession().getAttribute("existUser");
System.out.println("1 existUser:" + existUser);
if (null == existUser) {
// 不在登陆状态,进行自动登陆
// 获取 Cookie
Cookie cookie = CookieUtils.getCookie(request.getCookies(), "autoLogin");
// 判断 Cookie 是否为空,存在浏览器被清理掉缓存问题
if (null == cookie) {
// 浏览器清理缓存,相当于自动登陆失败!跳转到登陆页面,进行手动登陆。
request.getRequestDispatcher("/login.jsp").forward(request, resp);
} else {
// 缓存还存在,进行自动登陆
// 获取用户信息
String[] infos = cookie.getValue().split("-");
String username = infos[0];
String password = infos[1];
System.out.println("3 username="+username+",password="+password);
if ("root".equals(username) && "1234".equals(password)) {
// 自动登陆成功,修改登陆状态,
System.out.println("自动登陆成功");
existUser = new Userinfo();
existUser.setUsername(username);
existUser.setPassword(password);
request.getSession().setAttribute("existUser", existUser);
chain.doFilter(req, resp);
} else {
// 自动登陆失败(修改了密码)
System.out.println("自动登陆失败");
request.getRequestDispatcher("/login.jsp").forward(request, resp);
}
}
} else {
// 在登陆状态,直接放行
chain.doFilter(req, resp);
}
}
}
@Override
public void init(FilterConfig config) throws ServletException {
}
}
ShowIndexServlet.java
@WebServlet(name = "ShowIndexServlet", urlPatterns = "/showIndex")
public class ShowIndexServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Userinfo existUser = (Userinfo) request.getSession().getAttribute("existUser");
StringBuffer respBody = new StringBuffer();
if (null == existUser) {
// 不在登陆状态
respBody.append("您没有登陆, <a href='/demo/login.jsp'>请登陆</a>");
} else {
// 还在登陆状态
respBody.append("欢迎回来,").append(existUser.getUsername());
}
response.getWriter().write(respBody.toString());
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
web.xml
<filter>
<filter-name>AutoLoginFilter</filter-name>
<filter-class>com.demo.filter.AutoLoginFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>AutoLoginFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
2.2 案例:Filter 过滤器实现敏感词过滤
// 正常情况最好将注解信息配置在 web.xml 中
@WebFilter(
filterName = "SensitiveWordsFilter",
urlPatterns = "/*",
initParams = {
@WebInitParam(name="word1", value="笨蛋"),
@WebInitParam(name="word2", value="傻蛋"),
@WebInitParam(name="word3", value="蠢蛋")
}
)
public class SensitiveWordsFilter implements Filter {
// 敏感词
private List<String> sensitiveWords = new ArrayList<>();
@Override
public void init(FilterConfig config) throws ServletException {
Enumeration<String> parameterNames = config.getInitParameterNames();
while (parameterNames.hasMoreElements()) {
String sensitiveWord = config.getInitParameter(parameterNames.nextElement());
sensitiveWords.add(sensitiveWord);
}
}
@Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
HttpServletRequest request = (HttpServletRequest) req;
// 增强 request 的 getParameter 方法
HttpServletRequest requestProxy = (HttpServletRequest) Proxy.newProxyInstance(
request.getClass().getClassLoader(),
request.getClass().getInterfaces(),
(proxy, method, args) -> {
Object returnValue = null;
String methodName = method.getName();
if ("getParameter".equals(methodName)) {
// returnValue 就是 getParameter 方法的返回值,需要处理敏感词
String word = (String) method.invoke(request, args);
// 处理敏感词
for (String sensitiveWord : sensitiveWords) {
if (word.contains(sensitiveWord)) {
// 处理敏感词
returnValue = word.replace(sensitiveWord, "***");
}
}
} else {
returnValue = method.invoke(request, args);
}
return returnValue;
}
);
// 放行增强的请求对象 requestProxy(在 Servlet 中可以去使用到过滤后的)
chain.doFilter(requestProxy, resp);
}
@Override
public void destroy() { }
}