Spring Boot 使用过滤器、拦截器、监听器

前言

作用

  • 过滤器(Filter):当有一堆请求,只希望符合预期的请求进来。
  • 拦截器(Interceptor):想要干涉预期的请求。
  • 监听器(Listener):想要监听这些请求具体做了什么。

区别

过滤器是在请求进入容器后,但还没有进入 Servlet 之前进行预处理的。如下图所示。

拦截器是在请求进入控制器(Controller) 之前进行预处理的。

虚线内就是过滤器和拦截器的作用范围:

过滤器依赖于 Servlet 容器,而拦截器依赖于 Spring 的 IoC 容器,因此可以通过注入的方式获取容器当中的对象。

监听器用于监听 Web 应用中某些对象的创建、销毁、增加、修改、删除等动作,然后做出相应的处理。

过滤器

  • 过滤敏感词汇(防止sql注入)
  • 设置字符编码
  • URL级别的权限访问控制
  • 压缩响应信息

过滤器的创建和销毁都由 Web 服务器负责,Web 应用程序启动的时候,创建过滤器对象,为后续的请求过滤做好准备。

过滤器可以有很多个,一个个过滤器组合起来就成了 FilterChain,也就是过滤器链。

在 Spring 中,过滤器都默认继承了 OncePerRequestFilter,顾名思义,OncePerRequestFilter 的作用就是确保一次请求只通过一次过滤器,而不重复执行。

接下来我们通过继承 OncePerRequestFilter 来实现 JWT 登录授权过滤。

 

public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {@Overrideprotected void doFilterInternal(HttpServletRequest request,HttpServletResponse response,FilterChain chain) throws ServletException, IOException {// 从客户端请求中获取 JWTString authHeader = request.getHeader(this.tokenHeader);// 该 JWT 是我们规定的格式,以 tokenHead 开头if (authHeader != null && authHeader.startsWith(this.tokenHead)) {// The part after "Bearer "String authToken = authHeader.substring(this.tokenHead.length());// 从 JWT 中获取用户名String username = jwtTokenUtil.getUserNameFromToken(authToken);LOGGER.info("checking username:{}", username);// SecurityContextHolder 是 SpringSecurity 的一个工具类// 保存应用程序中当前使用人的安全上下文if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {// 根据用户名获取登录用户信息UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);// 验证 token 是否过期if (jwtTokenUtil.validateToken(authToken, userDetails)) {// 将登录用户保存到安全上下文中UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails,null, userDetails.getAuthorities());authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));SecurityContextHolder.getContext().setAuthentication(authentication);}}}chain.doFilter(request, response);}
}

新建一个 Web 项目 codingmore-filter-interceptor-listener。

添加一个过滤器 MyFilter :

@WebFilter(urlPatterns = "/*", filterName = "myFilter")
public class MyFilter implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {Filter.super.init(filterConfig);}@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {long start = System.currentTimeMillis();chain.doFilter(request,response);System.out.println("Execute cost="+(System.currentTimeMillis()-start));}@Overridepublic void destroy() {Filter.super.destroy();}
}

@WebFilter 注解用于将一个类声明为过滤器,urlPatterns 属性用来指定过滤器的 URL 匹配模式,filterName 用来定义过滤器的名字。

MyFilter 过滤器的逻辑非常简单,重写了 Filter 的三个方法,在 doFilter 方法中加入了时间戳的记录。

然后我们在项目入口类上加上 @ServletComponentScan 注解,这样过滤器就会自动注册。

启动服务器,访问任意的 URL。

拦截器

  • 登录验证,判断用户是否登录
  • 权限验证,判断用户是否有权限访问资源,如校验token
  • 日志记录,记录请求操作日志(用户ip,访问时间等),以便统计请求访问量
  • 处理cookie、本地化、国际化、主题等
  • 性能监控,监控请求处理时长等

我们来写一个简单的拦截器 LoggerInterceptor:

@Slf4j
public class LoggerInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {log.info("preHandle{} ...." ,request.getRequestURI());}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {HandlerInterceptor.super.afterCompletion(request, response, handler, ex);}}

一个拦截器必须实现 HandlerInterceptor 接口,preHandle 方法是 Controller 方法调用前执行,postHandle  是 Controller 方法正常返回后执行,afterCompletion 方法无论 Controller 方法是否抛异常都会执行。

只有 preHandle 返回 true 的话,其他两个方法才会执行。

如果 preHandle 返回 false 的话,表示不需要调用Controller方法继续处理了,通常在认证或者安全检查失败时直接返回错误响应。

再来一个 InterceptorConfig 对拦截器进行配置:

/*** @ClAssName InterceptorConfig* @Description TOOO* @Author soshi是神仙* @Date 2024/3/27 18:{MINUTE}*/
@Configuration
public class InterceptorConfig  implements WebMvcConfigurer {@Overridepublic void addInterceptors(InterceptorRegistry registry) {WebMvcConfigurer.super.addInterceptors(registry);registry.addInterceptor(new LoggerInterceptor()).addPathPatterns("/**");}
}

@Configuration 注解用于定义配置类,干掉了以往 Spring 繁琐的 xml 配置文件。

编写一个用于被拦截的控制器 MyInterceptorController:

@RestController
@RequestMapping("/myinterceptor")
public class MyInterceptorController {@RequestMapping("/hello")public  String hello(){return "测试拦截器的使用";}
}

@RestController 注解相当于 @Controller + @ResponseBody 注解,@ResponseBody 注解用于将 Controller 方法返回的对象,通过适当的 HttpMessageConverter 转换为指定格式后,写入到 Response 对象的 body 数据区,通常用来返回 JSON 或者 XML 数据,返回 JSON 数据的情况比较多。

启动服务器,访问 http://localhost:8080/myinterceptor/hello

在控制台可以看到拦截器中的日志信息

无论是过滤器还是拦截器,都属于AOP(面向切面编程)思想的具体实现。除了这两种实现之外,还有另一种更灵活的AOP实现技术,即 Aspect,在实战项目里,你可以看到 Aspect 具体实现。

比如说统一日志切面 WebLogAspect,就是用来记录请求信息的。

@Aspect
@Component
@Order(1)
public class WebLogAspect {private static final Logger LOGGER = LoggerFactory.getLogger(WebLogAspect.class);@Pointcut("execution(public * com.codingmore.controller.*.*(..))")public void webLog() {}@Before("webLog()")public void doBefore(JoinPoint joinPoint) throws Throwable {}@AfterReturning(value = "webLog()", returning = "ret")public void doAfterReturning(Object ret) throws Throwable {}@Around("webLog()")public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {long startTime = System.currentTimeMillis();//获取当前请求对象ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();webLog.setStartTime(startTime);webLog.setUri(request.getRequestURI());logMap.put("parameter",webLog.getParameter());logMap.put("spendTime",webLog.getSpendTime());logMap.put("description",webLog.getDescription());LOGGER.info("{}", JSONUtil.parse(webLog));return result;}/*** 根据方法和传入的参数获取请求参数*/private Object getParameter(Method method, Object[] args) {}
}

监听器

根据监听对象可以把监听器分为 3 类:

  • ServletContext:对应应用 application,整个 Web 服务器中只有一个,Web 服务器关闭时销毁。可用于数据缓存,例如结合redis,在Web服务创建时从数据库拉取数据到缓存服务器。
  • HttpSession: 对应会话 session,在会话建立时创建,一端会话关闭时销毁。可用于获取在线用户数量。
  • ServletRequest:对应 request,客户端发送请求时创建,一同创建的还有 response,用于封装请求数据,在一次请求处理完成时销毁。可用于封装用户信息。

新建一个 MyListener:

@WebListener
public class MyListener implements ServletContextListener {@Overridepublic void contextInitialized(ServletContextEvent sce) {ServletContextListener.super.contextInitialized(sce);System.out.println("上下文创建");}@Overridepublic void contextDestroyed(ServletContextEvent sce) {System.out.println("上下文销毁");}

@WebListener 注解用于将一个类声明为监听器,同样干掉了 web.xml 文件。

ServletContextListener 能够监听整个 Web 应用程序的生命周期。当 Web 应用启动时触发 contextInitialized 方法,关闭时触发 contextDestroyed 方法。

在 Intellij IDEA 中重启服务的时候,可以在控制台看到如下信息:

不过需要注意的是,在 Intellij IDEA 中直接关闭进程无法看到 contextDestroyed 被调用的消息。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/774607.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【数据结构】树、二叉树与堆(长期维护)

下面是关于树、二叉树、堆的一些知识分享,有需要借鉴即可。 一、初识树(了解即可) 1.树的概念 概念:一种非线性数据结构,逻辑形态上类似倒挂的树 树的构成:由一个根左子树右子树构成,其中子树…

unity学习(70)——编译游戏发生错误2

1.全屏问题其实无所谓,windows用tab可以切出来的。 2.现在主要问题是服务器try了以后虽然不崩溃了,但不再显示2个实例对象了,unity和exe此时都只能看到一个实例对象 2.1把之前报错位置的try-catch先注释掉 2.2 unity中此时登录666账号&…

leetcode代码记录(完全二叉树的节点个数

目录 1. 题目:2. 我的代码:小结: 1. 题目: 给你一棵 完全二叉树 的根节点 root ,求出该树的节点个数。 完全二叉树 的定义如下:在完全二叉树中,除了最底层节点可能没填满外,其余每层…

洛谷_P5019 [NOIP2018 提高组] 铺设道路_python写法

P5019 [NOIP2018 提高组] 铺设道路 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) n int(input()) data list(map(int,input().split()))ans 0 for i in range(1,n):if data[i]>data[i-1]:ans data[i]-data[i-1]print(ansdata[0])

python在运行时控制台以表格形式输出结果prettytable.PrettyTable()

使用prettytable库按表格的形式美化输出结果 效果如图: 表格中可接收列表格式的数据,列表中装字符串 # 引入模块 import prettytable as pt# 创建表格与表头,包含五列,分别为train-epoch,class,precisio…

Day02-DDLDMLDQL(定义,操作,查询)(联合查询,子查询,字符集和校对集,MySQL5.7乱码问题)

文章目录 Day02-DDL&DML和DQL学习目标1. SQL语言的组成2. DDL2.1 数据库结构2.2 表结构2.3 约束2.3.1 主键约束(重要)(1)特点(2) 添加主键(3)删除主键(了解) 2.3.2 自增约束(1)特点(2) 添加自增约束(3)删除自增约束(了解) 2.3.3 非空约束(1)添加非空约束(2) 删除非空约束 2…

Midjourney辞典AIGC中英双语图文辞典+Midjourney提示关键词

完整内容下载:https://download.csdn.net/download/u010564801/89042077 完整内容下载:https://download.csdn.net/download/u010564801/89042077 完整内容下载:https://download.csdn.net/download/u010564801/89042077

javaSwing坦克大战游戏

在游戏开发领域,坦克大战是一款经典的游戏,其简单而又耐玩的玩法吸引了无数玩家。而今,在Java编程技术的支持下,我们可以用Java Swing技术轻松实现这款经典游戏。本文将介绍如何使用Java Swing技术编写坦克大战游戏,并…

【Java程序设计】【C00352】基于Springboot的疫情隔壁酒店管理系统(有论文)

基于Springboot的疫情隔壁酒店管理系统(有论文) 项目简介项目获取开发环境项目技术运行截图 项目简介 项目获取 🍅文末点击卡片获取源码🍅 开发环境 运行环境:推荐jdk1.8; 开发工具:eclipse以…

VS code中安装了git插件,报错无法使用怎么办?

你好,我是云桃桃。 一个希望帮助更多朋友快速入门 WEB 前端程序媛。 1枚程序媛,2年时间从1800到月入过万,工作5年买房。 分享成长心得❤️,和你一起慢慢变富。 后台回复“前端工具”可获取开发工具,持续更新中 后台…

如何在Linux Ubuntu系统安装Nginx服务并实现无公网IP远程连接

文章目录 1. 安装Docker2. 使用Docker拉取Nginx镜像3. 创建并启动Nginx容器4. 本地连接测试5. 公网远程访问本地Nginx5.1 内网穿透工具安装5.2 创建远程连接公网地址5.3 使用固定公网地址远程访问 在开发人员的工作中,公网远程访问内网是其必备的技术需求之一。对于…

【Java程序设计】【C00387】基于(JavaWeb)Springboot的校园食堂订餐系统(有论文)

基于(JavaWeb)Springboot的校园食堂订餐系统(有论文) 项目简介项目获取开发环境项目技术运行截图 博主介绍:java高级开发,从事互联网行业六年,已经做了六年的毕业设计程序开发,开发过…

【LeetCode: 2580. 统计将重叠区间合并成组的方案数 + 合并区间】

🚀 算法题 🚀 🌲 算法刷题专栏 | 面试必备算法 | 面试高频算法 🍀 🌲 越难的东西,越要努力坚持,因为它具有很高的价值,算法就是这样✨ 🌲 作者简介:硕风和炜,…

基于Springboot的疫情隔离酒店管理系统(有报告)。Javaee项目,springboot项目。

演示视频: 基于Springboot的疫情隔离酒店管理系统(有报告)。Javaee项目,springboot项目。 项目介绍: 采用M(model)V(view)C(controller)三层体系…

4个AI智能写作平台,为你解决文章写作问题

在信息时代,写作是一项重要的技能,无论是在学术领域还是商务环境中。然而,对于许多人来说,写作并不是一件轻松的事情。但是,随着人工智能的发展,有许多AI智能写作平台可以帮助我们解决文章写作问题。在本文…

外包干了5年,技术退步明显.......

先说一下自己的情况,大专生,18年通过校招进入杭州某软件公司,干了接近4年的功能测试,今年年初,感觉自己不能够在这样下去了,长时间呆在一个舒适的环境会让一个人堕落! 而我已经在一个企业干了四年的功能测…

【MySQL】11. 复合查询(重点)

4. 子查询 子查询是指嵌入在其他sql语句中的select语句,也叫嵌套查询 4.1 单行子查询 返回一行记录的子查询 显示SMITH同一部门的员工 mysql> select * from emp where deptno (select deptno from emp where ename SMITH); -----------------------------…

Google AI 肺癌筛查的计算机辅助诊断

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗?订阅我们的简报,深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同,从行业内部的深度分析和实用指南中受益。不要错过这个机会,成为AI领…

计算机网络21-40

计算机网络21-40 以下是本文参考的资料 欢迎大家查收原版 本版本仅作个人笔记使用21、HTTPS是如何保证数据传输的安全,整体的流程是什么?(SSL是怎么工作保证安全)对称加密非对称加密消息认证码 (MAC)数字证书 22、如何保证公钥不被…

ABC346 A-G 题解

ABC346 A-G题解 A题目AC Code:时间复杂度 B题目时间复杂度AC Code: C题目时间复杂度AC Code: D题目时间复杂度AC Code: E题目时间复杂度AC Code: F题目时间复杂度AC Code: G题目时间复杂度AC Code&#xff…