拦截器和过滤器的使用场景?
拦截器本质上是面向切面编程(AOP),符合横切关注点的功能都可以放在拦截器中来实现,主要的应用场景包括:
1.登录验证,判断用户是否登录
2.权限验证,判断用户是否有权限访问资源,如校验token
3.日志记录,记录请求操作日志(用户ip,访问时间等),以便统计请求访问量
4.处理cookie、本地化、国际化、主题等
5.性能监控:监控请求的处理时长等
通用行为:读取cookie得到用户信息并将用户对象放入请求,以便后续流程使用,还有提取Locale、Theme等信息,只要是多个处理器需要的即可使用拦截器实现
过滤器本质是函数回调(职责链),主要的应用场景包括:
1.过滤敏感词汇
2.设置字符编码
3.URL级别的权限访问控制
4.压缩响应信息
过滤器原理
当我们使用过滤器时,过滤器会对几乎所有的请求进行过滤,过滤器可以动态的分为三个部分:
- 1.请求进入Tomcat等容器前:对请求进行第一次过滤,然后再执行下面的代码
- 2.过滤器将请求放行,如果还有其他过滤器,那么就继续交给下一个过滤器
- 3.Servlet处理完之后:对响应的内容进行再次过滤
自定义过滤器(IDEA)
1.创建一个普通的Maven项目,导入相关依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>3.0.4</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>3.0.4</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
</dependencies>
2.为了测试,创建一个输入评论的网页
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>评论</title>
</head>
<body>
<h1>输入评论内容</h1>
<form th:action="@{/comment/submit}" method="post">
<textarea name="message" cols="30" rows="10"></textarea>
<input type="submit" value="提交">
</form>
<p th:text="${comment}"></p>
</body>
</html>
3.创建一个filter包,以后我们的过滤器就放在这个包下,然后创建CommentFilter过滤器,实现Filter接口,通过@WebFilter注解,进行一些简单的配置
@WebFilter 用于将一个类声明为过滤器,该注解将会在部署时被容器处理,容器将根据具体的属性配置将相应的类部署为过滤器。
下面是一些属性解释:
- filterName:过滤器名
- urlPatterns:过滤器的URL匹配模式,这里可以配置你要过滤的请求
- initParam:设置一些初始参数
注意:jakarta.servlet.Filter接口,不要导错包啦
package com.demo.filter;
import jakarta.servlet.*;
import jakarta.servlet.annotation.WebFilter;
import jakarta.servlet.annotation.WebInitParam;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
@WebFilter(filterName = "commentFilter",urlPatterns = "/comment/submit",initParams = {@WebInitParam(name = "sensitiveWord", value = "nt")})
public class CommentFilter implements Filter {
private List<String> sensitiveWords = new ArrayList<>();
@Override
public void init(FilterConfig filterConfig) throws ServletException {
//得到敏感词汇
String word = filterConfig.getInitParameter("sensitiveWord");
//加入集合
sensitiveWords.add(word);
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
//设置编码
servletRequest.setCharacterEncoding("utf-8");
servletResponse.setContentType("text/html;charset=utf-8");
//得到评论
String message = servletRequest.getParameter("message");
for (String sensitiveWord : sensitiveWords) {
//对所有敏感词汇进行过滤
if (message.contains(sensitiveWord)){
//替换敏感词汇
message = message.replace(sensitiveWord, "**");
}
}
//存入request域
servletRequest.setAttribute("comment",message);
//放行
filterChain.doFilter(servletRequest,servletResponse);
}
}
4.创建一个controller软件包,在包下创建一个CommentController来处理请求
package com.demo.controller;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class CommentController {
/**
* 跳转到评论网页
*/
@RequestMapping("/comments")
public String toCommentPage() {
return "comment";
}
/**
* 处理提交评论的请求
* @param request
* @param model
* @return
*/
@PostMapping("/comment/submit")
public String getString(HttpServletRequest request, Model model) {
//获取过滤器设置的Attribute
String comment = (String) request.getAttribute("comment");
//将其封装在model里面,以便前端使用Thymeleaf模板获取到
model.addAttribute("comment", request.getAttribute("comment"));
return "comment";
}
}
5.创建启动类,启动测试!
@ServletComponentScan:将过滤器注册到Spring容器中
package com.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;
@SpringBootApplication
@ServletComponentScan
public class ApplicationDemo {
public static void main(String[] args) {
SpringApplication.run(ApplicationDemo.class,args);
}
}
6.访问 http://localhost:8080/comments 网页,故意输入敏感词汇:(可以输入"我是nt"),点击提交,可以看到过滤器的处理结果。
过滤器的生命周期
Filter有三个阶段:
- 1.初始化阶段:当服务器启动时,我们的服务器(Tomcat)就会读取配置文件,扫描注解,然后来创建Filter
- 2.拦截和过滤节点:只要请求资源的路径和拦截的路径相同,那么过滤器就会对请求进行过滤,这个阶段在服务器运行过程中会一直循环
- 3.销毁阶段:当服务器(Tomcat)关闭时,服务器创建的Filter也会随之销毁
可以测试一下:
package com.demo.filter;
import jakarta.servlet.*;
import jakarta.servlet.annotation.WebFilter;
import java.io.IOException;
@WebFilter(filterName = "BFilter",urlPatterns = "/*")
public class BFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("过滤器初始化");
Filter.super.init(filterConfig);
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
System.out.println("BFilter处理请求~");
chain.doFilter(request,response);
System.out.println("BFilter处理响应~");
}
@Override
public void destroy() {
System.out.println("过滤器销毁啦");
Filter.super.destroy();
}
}
测试结果如下:
多个Filter的执行顺序
如果我们是在web.xml中配置的过滤器,那么过滤器的执行顺序就是<filter-mapping>在web的配置顺序,配置在前就会先执行
若我们是使用注解@WebFilter进行配置的,那么执行顺序就是按照过滤器类的字典顺序来执行的,比如AFilter和BFilter过滤器,就会先执行AFilter,因为AFilter字典序在前。
如果是注解和xml混合使用,那么在web.xml中配置的先执行。
拦截器实现
1.在上面的环境下,新建软件包interceptor,专门放拦截器类,创建一个MyInterceptor拦截器
package com.demo.interceptor;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
@Component
public class MyInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("拦截器前置处理~");
return HandlerInterceptor.super.preHandle(request, response, handler);
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("拦截器后置处理~");
}
}
2.写一个配置类,注册我们自定义的拦截器:新建一个config包,专门放我们的配置类
package com.demo.config;
import com.demo.interceptor.MyInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Autowired
MyInterceptor myInterceptor;
/**
* 配置静态资源,比如html,js,css等
* @param registry
*/
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
}
/**
* 注册拦截器,我们自定义的拦截器需要在这里注册才会生效
* @param registry
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(myInterceptor).addPathPatterns("/**").excludePathPatterns();
}
}
3.启动测试,输入:localhost:8080/,查看输出结果
拦截器的工作原理
一个拦截器,只有preHandle方法返回true,postHandle、afterCompletion才有可能被执行,如果preHandle方法返回false,那么该拦截器的postHandle、afterCompletion必然不会被执行。
所有的拦截器(Interceptor)和处理器(Handler)都注册在HandleMapping中
Spring MVC中所有的请求都是由DispatcherServlet分发的
当请求进入DispatcherServlet.doDispatch()的时候,首先会得到处理该请求的Handler(即Controller中对应的方法)以及所有拦截该请求的拦截器,拦截器就是在这里被调用开始工作的
拦截器正常的工作流程
中断流程
如果在Interceptor1.preHandle中报错或返回false ,那么接下来的流程就会被中断,但注意被执行过的拦截器的afterCompletion仍然会执行。
拦截器中的三个方法:
preHandle()方法:该方法会在控制方法前执行,方法返回值表示是否知道如何写一个接口。中断后续操作。当其返回值为true时,表示继续向下执行;当其返回值为false时,会中断后续的所有操作(包括调用下一个拦截器和控制器类中的方法执行等)
postHandle()方法:该方法会在控制器方法调用之后,且解析视图之前执行。可以通过此方法对请求域中的模型和视图做出进一步修改
afterCompletion()方法:该方法会在整个请求完成,即视图渲染结束之后执行。可以通过此方法实现一些资源清理、记录日志信息等工作
拦截器和过滤器的区别
1、实现原理不同
过滤器的本质是函数回调
拦截器是基于java反射机制(动态代理),本质是面向切面编程
2、使用范围不同
我们看到过滤器(Filter)实现的是jakarta.servlet.Filter接口,而这个接口是在Servlet规范中定义的,也就是意味着过滤器使用要依赖Tomcat等容器,导致它只能在web应用中使用
而拦截器(Interceptor)是Spring的一个组件,并由Spring容器管理,不依赖Tomcat等容器,可以单独使用。不仅能应用在web程序中,也可以用于Application/Swing等程序中
3、触发时机不同
过滤器:在请求进入Servlet之前被触发,在响应返回视图之前也会被触发一次
拦截器:在请求进入Servlet之后,进入Controller之前被触发
4.处理的范围不同
过滤器几乎可以对所有请求进行拦截,而拦截器知会对要进入Controller层请求资源的请求进行拦截
感谢下面大佬的文章:
(69 封私信 / 2 条消息) Spring 拦截器和过滤器的区别? - 知乎 (zhihu.com)https://www.zhihu.com/question/30212464/answer/1786967139什么是拦截器?拦截器如何配置? - 北根娃 - 博客园 (cnblogs.com)https://www.cnblogs.com/alanlin/p/16267497.html(18条消息) JavaWeb过滤器(Filter)详解,是时候该把过滤器彻底搞懂了(万字说明)_webfilter_秃头披风侠.的博客-CSDN博客https://blog.csdn.net/m0_51545690/article/details/123677340Springboot过滤器和拦截器详解及使用场景 - 知乎 (zhihu.com)https://zhuanlan.zhihu.com/p/340397290#:~:text=%E6%8B%A6%E6%88%AA%E5%99%A8%E5%BA%94%E7%94%A8%E5%9C%BA%E6%99%AF%201%20%E7%99%BB%E5%BD%95%E9%AA%8C%E8%AF%81%EF%BC%8C%E5%88%A4%E6%96%AD%E7%94%A8%E6%88%B7%E6%98%AF%E5%90%A6%E7%99%BB%E5%BD%95%E3%80%82%202%20%E6%9D%83%E9%99%90%E9%AA%8C%E8%AF%81%EF%BC%8C%E5%88%A4%E6%96%AD%E7%94%A8%E6%88%B7%E6%98%AF%E5%90%A6%E6%9C%89%E6%9D%83%E9%99%90%E8%AE%BF%E9%97%AE%E8%B5%84%E6%BA%90%EF%BC%8C%E5%A6%82%E6%A0%A1%E9%AA%8Ctoken%203,%E6%97%A5%E5%BF%97%E8%AE%B0%E5%BD%95%EF%BC%8C%E8%AE%B0%E5%BD%95%E8%AF%B7%E6%B1%82%E6%93%8D%E4%BD%9C%E6%97%A5%E5%BF%97%EF%BC%88%E7%94%A8%E6%88%B7ip%EF%BC%8C%E8%AE%BF%E9%97%AE%E6%97%B6%E9%97%B4%E7%AD%89%EF%BC%89%EF%BC%8C%E4%BB%A5%E4%BE%BF%E7%BB%9F%E8%AE%A1%E8%AF%B7%E6%B1%82%E8%AE%BF%E9%97%AE%E9%87%8F%E3%80%82%204%20%E5%A4%84%E7%90%86cookie%E3%80%81%E6%9C%AC%E5%9C%B0%E5%8C%96%E3%80%81%E5%9B%BD%E9%99%85%E5%8C%96%E3%80%81%E4%B8%BB%E9%A2%98%E7%AD%89%E3%80%82%205%20%E6%80%A7%E8%83%BD%E7%9B%91%E6%8E%A7%EF%BC%8C%E7%9B%91%E6%8E%A7%E8%AF%B7%E6%B1%82%E5%A4%84%E7%90%86%E6%97%B6%E9%95%BF%E7%AD%89%E3%80%82%206%20%E9%80%9A%E7%94%A8%E8%A1%8C%E4%B8%BA%EF%BC%9A%E8%AF%BB%E5%8F%96cookie%E5%BE%97%E5%88%B0%E7%94%A8%E6%88%B7%E4%BF%A1%E6%81%AF%E5%B9%B6%E5%B0%86%E7%94%A8%E6%88%B7%E5%AF%B9%E8%B1%A1%E6%94%BE%E5%85%A5%E8%AF%B7%E6%B1%82%EF%BC%8C%E4%BB%8E%E8%80%8C%E6%96%B9%E4%BE%BF%E5%90%8E%E7%BB%AD%E6%B5%81%E7%A8%8B%E4%BD%BF%E7%94%A8%EF%BC%8C%E8%BF%98%E6%9C%89%E5%A6%82%E6%8F%90%E5%8F%96Locale%E3%80%81Theme%E4%BF%A1%E6%81%AF%E7%AD%89%EF%BC%8C%E5%8F%AA%E8%A6%81%E6%98%AF%E5%A4%9A%E4%B8%AA%E5%A4%84%E7%90%86%E5%99%A8%E9%83%BD%E9%9C%80%E8%A6%81%E7%9A%84%E5%8D%B3%E5%8F%AF%E4%BD%BF%E7%94%A8%E6%8B%A6%E6%88%AA%E5%99%A8%E5%AE%9E%E7%8E%B0%EF%BC%89