【java】Filter --- 过滤器
- 1. 什么是Filter
- 2. Filter的功能
- 3. Filter的种类
- 4. Filter的简单使用
- 4.1 使用注解方式
- 4.2 使用web.xml配置文件
- 5. Filter的细节
- 5.1 过滤器执行流程
- 5.2 过滤器生命周期方法
- 5.3 过滤器配置详解
- 6. Filter链
- 7. Filter案例1:登录验证
- 8. Filter案例2:敏感词汇过滤(通过代理模式)
1. 什么是Filter
Filter是Java Web三大组件之一,它主要用于对用户请求(HttpServletRequest)进行预处理,也可以对服务器响应(HttpServletResponse)进行后处理,是个典型的处理链。
- Filter与Servlet的区别在于:
它不能直接向用户生成响应。 - 完整的流程是:Filter对用户请求进行预处理,接着将请求交给Servlet进行处理并生成响应,最后Filter再对服务器响应进行后处理。
Filter在英文中是过滤器的意思,当然在此处的使用也是完美的切合了它的意思,我们使用filter的主要目的就是完成一个过滤的作用。可以在一个请求到达servlet之前,将其截取进行逻辑判断,然后决定是否放行到请求的servlet。也可以在一个response到达客户端之前,截取结果进行逻辑判断,然后决定是否允许返回给客户端。
最后需要注意的是,一个filter过滤器可以加在多个servlet控制器上,当然多个filter过滤器也是可以加在一个servlet控制器上的。由此也是可以看出来,我们使用filter往往是对一些公共的操作进行处理,例如:判断用户权限,解码本次请求等,还比如,我们的web应用中某些页面是需要用户登录后才能访问的,以往我们都是在每个servlet页面加上判断控制,导致代码冗余,有了filter,我们可以定义一个实现了filter的过滤器,让需要判断是否登录的页面都加上这么一个过滤器,可以大大降低代码的冗余程度。
2. Filter的功能
在HttpServletRequest到达 Servlet 之前,拦截客户的HttpServletRequest 。根据需要检查HttpServletRequest,也可以修改HttpServletRequest 头和数据。
在HttpServletResponse到达客户端之前,拦截HttpServletResponse 。根据需要检查HttpServletResponse,也可以修改HttpServletResponse头和数据。
- 应用场景
- 登录验证
- 统一设置编码格式
- 访问权限控制
- 敏感字符过滤等
- 等等…
3. Filter的种类
- 用户授权的Filter:Filter负责检查用户请求,根据请求过滤用户非法请求。
- 日志Filter:详细记录某些特殊的用户请求。
- 负责解码的Filter:包括对非标准编码的请求解码。
- Filter可拦截多个请求或响应;
4. Filter的简单使用
4.1 使用注解方式
package cn.siyi.web.filter;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
@WebFilter("/*")//访问所有资源之前,都会执行该过滤器
public class FilterDemo1 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("filterDemo1...");
//放行
filterChain.doFilter(servletRequest,servletResponse);
}
@Override
public void destroy() {
}
}
4.2 使用web.xml配置文件
package cn.siyi.web.filter;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
public class FilterDemo1 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("filterDemo1...");
//放行
filterChain.doFilter(servletRequest,servletResponse);
}
@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_4_0.xsd"
version="4.0">
<filter>
<filter-name>demo1</filter-name>//过滤器名称
<filter-class>cn.siyi.web.filter.FilterDemo1</filter-class>//过滤器类的包路径
<!-- <init—param> //可选 -->
<!-- <param—name>参数名</param-name>//过滤器初始化参数 -->
<!-- <param-value>参数值</param-value> -->
<!-- </init—pamm> -->
</filter>
<filter-mapping> //过滤器映射
<filter-name>demo1</filter-name>
<url-pattern>/*</url-pattern>//指定过滤器作用的对象
</filter-mapping>
</web-app>
5. Filter的细节
5.1 过滤器执行流程
- 执行过滤器
- 执行放行后的资源
- 回来执行过滤器放行代码下边的代码
5.2 过滤器生命周期方法
init(FilterConfig filterConfig)
代表filter对象初始化方法,filter对象创建时执行
doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
代表filter执行过滤的核心方法,如果某资源在已经被配置到这个filter进行过滤的话,
那么每次访问这个资源都会执行doFilter方法
destory()代表是filter销毁方法,当filter对象销毁时执行该方法
5.3 过滤器配置详解
- 拦截路径配置:
1. 具体资源路径: /index.jsp 只有访问index.jsp资源时,过滤器才会被执行
2. 拦截目录: /user/* 访问/user下的所有资源时,过滤器都会被执行
3. 后缀名拦截: *.jsp 访问所有后缀名为jsp资源时,过滤器都会被执行
4. 拦截所有资源: /* 访问所有资源时,过滤器都会被执行
- 拦截方式配置:资源被访问的方式
- 注解配置:
* 设置dispatcherTypes属性
1. REQUEST:默认值。浏览器直接请求资源
2. FORWARD:转发访问资源
3. INCLUDE:包含访问资源
4. ERROR:错误跳转资源
5. ASYNC:异步访问资源
- web.xml配置
* 设置<dispatcher></dispatcher>标签即可//值和上面相同
6. Filter链
在一个web应用中,可以开发编写多个Filter,这些Filter组合起来称之为一个Filter链。
web服务器根据Filter在web.xml文件中的注册顺序,决定先调用哪个Filter,当第一个Filter的doFilter方法被调用时,web服务器会创建一个代表Filter链的FilterChain对象传递给该方法。在doFilter方法中,开发人员如果调用了FilterChain对象的doFilter方法,则web服务器会检查FilterChain对象中是否还有filter,如果有,则调用第2个filter,如果没有,则调用目标资源。
- 执行顺序:如果有两个过滤器:过滤器1和过滤器2
- 过滤器1
- 过滤器2
- 资源执行
- 过滤器2
- 过滤器1
- 过滤器先后顺序问题:
- 注解配置:按照类名的字符串比较规则比较,值小的先执行
- 如: AFilter 和 BFilter,AFilter就先执行了。
- web.xml配置: 谁定义在上边,谁先执行
7. Filter案例1:登录验证
package cn.siyi.web.filter;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
/**
* 登录验证的过滤器
*/
//@WebFilter("/*")
public class LoginFilter implements Filter {
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
System.out.println(req);
//0.强制转换
HttpServletRequest request = (HttpServletRequest)req;
//1.获取资源请求路径
String uri = request.getRequestURI();
//2.判断是否包含登录相关资源路径
if(uri.contains("login.jsp") || uri.contains("/loginServlet") || uri.contains("/css/") || uri.contains("/js/") || uri.contains("/checkCodeServlet")||uri.contains("/fonts/")){
//包含,用户就是想登录。放行
chain.doFilter(req,resp);
}else{
//不包含,需要验证用户是否登录
//3.从session中获取user
Object user = request.getSession().getAttribute("user");
if(user != null){
//登录了,放行
chain.doFilter(req,resp);
}else{
//没有登录.跳转登录页面
request.setAttribute("login_msg","您尚未登录,请登录");
request.getRequestDispatcher("/login.jsp").forward(req,resp);
}
}
}
public void init(FilterConfig config) throws ServletException {
}
public void destroy() {
}
}
8. Filter案例2:敏感词汇过滤(通过代理模式)
package cn.siyi.web.filter;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.List;
@WebFilter("/*")
public class SensitiveWordsFilter implements Filter {
private List<String> list = new ArrayList<>();//敏感词汇
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
//1.创建代理对象,增强getParameter方法
ServletRequest proxy_req = (ServletRequest) Proxy.newProxyInstance(req.getClass().getClassLoader(), req.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//增强getParameter方法
if(method.getName().equals("getParameter")){
//增强放返回值
//获取返回值
String value = (String) method.invoke(req, args);
if(value != null){
for (String str:list){
if(value.contains(str)){
value= value.replaceAll(str,"***");
}
}
}
return value;
}
return method.invoke(req,args);
}
});
chain.doFilter(proxy_req, resp);
}
public void init(FilterConfig config) throws ServletException {
try {
//1.加载文件 获取文件真实路径
ServletContext servletContext = config.getServletContext();
String realPath = servletContext.getRealPath("/WEB-INF/classes/敏感词汇.txt");
//2.读取文件
BufferedReader bufferedReader = new BufferedReader(new FileReader(realPath));
//3.将文件的每一行添加到list中
String line=null;
while((line=bufferedReader.readLine())!=null){
list.add(line);
}
System.out.println(list);
bufferedReader.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
public void destroy() {
}
}