Filter : 过滤器
- web中的过滤器 :
- 当我们访问服务器的资源时 , 过滤器可以将请求拦截下来 , 完成一些特殊的功能 ,
- 过滤器一般是完成一些通用的操作 ,
- (比如登录的判断 , 只有登录了才能进行操作)
- 设置编码的操作
- 敏感字符的操作
Filter的快速入门 :
-
步骤 :
-
(1)定义一个类 , 实现一个接口 : Filter
-
public class FilterDemo1 implements Filter {}
-
-
(2)复写方法
-
(3)配置拦截资源 :
-
有两种配置方法:
-
①web.xml
-
②注解的方式 :
-
@WebFilter("/*")//访问所有资源之前 , 都要执行该过滤器 , 这里写的就是使用过滤器的资源路径 public class FilterDemo1 implements Filter {}
-
-
-
(4)过滤器执行之后 , 要考虑是否放行 ,
-
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { System.out.println("filterdemo1被执行了..."); //放行的代码实现 : filterChain.doFilter(servletRequest , servletResponse); }
-
Filter的细节 :
(1) web.xml配置
-
<filter> <filter-name>demo1</filter-name> <!--拦截器的路径--> <filter-class>cn.sichen.web.filter.FilterDemo1</filter-class> </filter> <filter-mapping> <filter-name>demo1</filter-name> <!--拦截路径--> <url-pattern>/*</url-pattern> </filter-mapping>
(2) 过滤器的执行流程
- ①执行过滤器
- ②执行放行后的资源 (位于放行语句前的位置)
- ③回来执行过滤器放行代码后的代码
(3) 过滤器的生命周期方法
-
/** * 每一次请求被拦截资源时 , 会执行 , 执行多次 * 具体的操作 * @param request * @param response * @param chain * @throws ServletException * @throws IOException */ @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException { System.out.println("doFilter..."); chain.doFilter(request, response); } /** * 在服务器启动后会创建Filter对象 , 然后调用init方法 * 一般用于加载资源 * @param config * @throws ServletException */ public void init(FilterConfig config) throws ServletException { System.out.println("init..."); } /** * 在服务器关闭之前的时候 , Filter对象被销毁 , * 如果服务器是正常关闭 , 会执行destroy方法 * 一般进行释放资源 */ public void destroy() { System.out.println("destroy..."); }
(4) 过滤器的配置详解
①拦截路径的配置
- 1.具体的资源路径 : /index.jsp 只有访问index.jsp资源时 . 过滤器才会被执行
- 2.目录拦截 : /user / * 访问 /user 下的所有资源时 , 过滤器会被执行
- 3.后缀名拦截 : *.jsp 访问所有后缀名为jsp的时候 , 过滤器都会被执行
- 4.拦截所有资源时 : / * 访问所有的资源时 , 过滤器都会被执行
②拦截方式的配置
-
资源被访问的方式 :
-
注解配置 :
-
设置 : dispatcherTypes属性
-
//表示浏览器直接请求资源的时候 , 只有请求访问index.jsp的时候才执行过滤 //@WebFilter(value = "/index.jsp" , dispatcherTypes = DispatcherType.REQUEST) //表示只有转发访问的的时候才执行 , @WebFilter(value = "/index.jsp" , dispatcherTypes = DispatcherType.FORWARD)
-
-
注意设置其他属性的时候 , 路径的前边要加value , 之前的是只有一个 , value省略了
- REQUEST : 默认值 浏览器直接请求资源
- FORWARD : 转发来访问资源
- INCLUDE : 包含访问资源
- ERROR : 错误跳转资源
- ASYNC : 异步访问
-
配置多个属性 :
-
@WebFilter(value = "/index.jsp" , dispatcherTypes = {DispatcherType.FORWARD , DispatcherType.REQUEST}) //将属性以 { } 括起来 , 中间用 , 隔开即可
-
-
在xml文件中配置 :
-
<filter-mapping> <filter-name>demo1</filter-name> <url-pattern>/*</url-pattern> <dispatcher>REQUEST</dispatcher> </filter-mapping>
-
(5) 过滤器链 (配置多个过滤器)
- 执行顺序 :
- 如果有两个过滤器 : 过滤器1 和 过滤器2
- 先执行过滤器1 ,
- 在执行过滤器2 ,
- 资源执行 ,
- 执行过滤器2 ,
- 执行过滤器1 .
- 如果有两个过滤器 : 过滤器1 和 过滤器2
- 过滤器的先后顺序问题 :
- 注解配置 :
- 按照类名的字符串比较规则去比较的 , 值小的先执行 , 按照总首字母开始比较的顺序 , 一次往后比较 , 知道出现一个区分大小的位置为止 .
- 如: AFilter 和 BFilter A<B 那么就是 AFilter先执行 ,
- 如 : Filter6 和 Filter7 就是 Filter6 先执行 ,
- 但是 如果是 : Filter6 和 Filter17 比较 , 就是Filter17先执行
- web.xml配置
- 谁的定义在上边 , 谁就先执行 ,
综合案例_登录验证 :
需求 :
- 访问 一个资源 , 验证其是否登录了 ,
- 如果登录了 , 则直接放行
- 如果没有登录 , 则跳转到登录页面 , 提示 :您尚未登录 , 请先登录
package cn.sichen.web.filter;
import jakarta.servlet.*;
import jakarta.servlet.annotation.*;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpSession;
import java.io.IOException;
/**
* 登录有关的Filter
*/
@WebFilter("/*")
public class LoginFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
//1.强制转换Http相关的request
HttpServletRequest request1 = (HttpServletRequest) request;
//2.获取资源的请求路径
String URI = request1.getRequestURI();
//1.判断是否包含登录相关的资源 , 要注意排除掉css/js/图片/验证码等资源
if (URI.contains("/login.jsp")||URI.contains("LoginServlet")||URI.contains("/css/")||URI.contains("/js/")||URI.contains("/fonts/")||URI.contains("/checkCodeServlet")){
//包含证明用户就是想登录 放行
chain.doFilter(request , response);
}else{
//不包含 : 需要验证用户是否登录
//3.从session中获取User
HttpSession session = request1.getSession();
Object main = session.getAttribute("main");
if (main != null){
//登录了 放行
chain.doFilter(request,response);
}else{
request.setAttribute("login_error" , "您尚未登录 , 请登录!");
request.getRequestDispatcher("/login.jsp").forward(request , response);
}
}
}
public void init(FilterConfig config) throws ServletException {
}
public void destroy() {
}
}
综合案例_敏感词汇过滤 :
- 对案例录入的数据进行敏感词汇过滤
- 如果是敏感词汇 , 替换为***
分析 :
1.对request对象进行增强 , 增强获取参数相关参数即可 ,
2.放行 : 传递的是代理对象 ,
package cn.sichen.web.filter;
import jakarta.servlet.*;
import jakarta.servlet.annotation.WebFilter;
import org.springframework.cglib.proxy.InvocationHandler;
import org.springframework.cglib.proxy.Proxy;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
/**
* 敏感词汇过滤器
*/
@WebFilter("/*")
public class SensitiveWordsFilter implements Filter {
//敏感词汇的list集合
private List<String> list = new ArrayList<String>();
public void init(FilterConfig config) throws ServletException {
try {
//1.加载敏感词汇的txt文件
InputStream is = SensitiveWordsFilter.class.getClassLoader().getResourceAsStream("SensitiveWords.txt");
//2.读取文件 注意 BufferedReader读取本地文件使用的编码是GBK的编码 , 所以使用本地文件的时候 , 也要将编码改为GBK
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(is));
//3.将文件的每一行数据添加到list中
String line = null;
//这里每次就是读取一行数据
while ((line = bufferedReader.readLine()) != null) {
list.add(line);
}
//关闭这个流
bufferedReader.close();
System.out.println(list);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void doFilter(final ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
//1.创建代理对象 , 增强 getParameter这个方法
ServletRequest proxy_request = (ServletRequest) Proxy.newProxyInstance(request.getClass().getClassLoader(), request.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//判断是否是getParameter这个方法
if (("getParameter").equals(method.getName())) {
//1.增强返回值 ,
//获取返回值
String value = (String) method.invoke(request, args);
if (value != null) {
for (String str : list) {
if (value.contains(str)) {
//替换敏感词汇
value = value.replaceAll(str, "***");
}
}
}
return value;
}
return method.invoke(request, args);
}
});
//2.放行 , 传递的是代理对象 ,
chain.doFilter(proxy_request, response);
}
public void destroy() {
}
}
Listener : 监听器
概念 : JavaWeb的三大组件之一
-
事件监听机制 :
- 事件 : 一件事情
- 事件源 : 事件发生的地方
- 监听器 : 一个对象
- 注册监听 : 将事件, 事件源 , 监听器 绑定在一起 , 当事件源上发生某个事件之后 , 执行监听器代码 ,
-
ServletContextListener : 监听ServletContext对象的创建和销毁
- void contextDestroyed(ServletContextEvent sce) : ServletContext对象被销毁之前会调用该方法
- void contextInitialized(ServletContextEvent sce) ; ServletContext对象创建之后会调用该方法
-
实现 :
-
步骤 :
- 1.定义一个类去实现 ServletContextListener 这个接口 ,
- 2.复写方法
- 3.配置
-
web.xml的配置方式 : (配置比较简单)
-
<!--配置监听器--> <listener> <listener-class>cn.sichen.web.listener.ContextListener</listener-class> </listener> <!--在xml文件中指定参数加载路径 , 也就是--> <context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/classes/applocationContext.xml</param-value> </context-param>
-
/** * 监听ServletContext对象创建的 , ServletContext对象在服务器启动之后自动创建 * 在服务器启动后自动调用 * @param sce */ @Override public void contextInitialized(ServletContextEvent sce) { /* This method is called when the servlet context is initialized(when the Web application is deployed). */ //加载资源文件 //1.获取ServletContext对象 ServletContext servletContext = sce.getServletContext(); //2.加载资源文件 String contextConfigLocation = servletContext.getInitParameter("contextConfigLocation"); //3.获取真实路径 String realPath = servletContext.getRealPath(contextConfigLocation); //4.加载进内存 try { FileInputStream fis = new FileInputStream(realPath); } catch (FileNotFoundException e) { e.printStackTrace(); } System.out.println("ServletContext这个对象被创建了..."); } /** * 在服务器关闭的时候 , ServletContext对象被销毁 , 当服务器正常关闭的时候 , 调用该方法 * @param sce */ @Override public void contextDestroyed(ServletContextEvent sce) { /* This method is called when the servlet Context is undeployed or Application Server shuts down. */ System.out.println("销毁ServletContext对象..."); }
-
-
注解的方式 : 这个方法不需要添加路径即可 ,直接加这个注解接口
-
@WebListener
-
-
调用该方法
* @param sce
/
@Override
public void contextDestroyed(ServletContextEvent sce) {
/ This method is called when the servlet Context is undeployed or Application Server shuts down. */
System.out.println(“销毁ServletContext对象…”);
}
```
-
注解的方式 : 这个方法不需要添加路径即可 ,直接加这个注解接口
-
@WebListener
-