登录认证(5):过滤器:Filter

统一拦截

上文我们提到(登录认证(4):令牌技术),现在大部分项目都使用JWT令牌来进行会话跟踪,来完成登录功能。有了JWT令牌可以标识用户的登录状态,但是完整的登录逻辑如图所示:

我们还需要一个统一拦截器,用于拦截用户的请求,在拦截器中,我们需要验证用户的JWT令牌,来判断用户是否登录,从而进行放行或拦截。统一拦截一般有两种解决方案:Filter过滤器Interceptor拦截器,本文先讲解Filter过滤器

Filter过滤器

工作原理

Filter的本意就是过滤器,在JavaWeb中是用于过滤请求的,是JavaWeb的三大核心组件之一 ServletFilterListenerFilter过滤器的工作原理就是将客户端发起的对服务端资源的请求拦截下来,进行处理,从而实现一些特殊功能

当程序使用了Filter过滤器之后,请求想要访问服务端的资源,就必须先经过过滤器,待过滤器处理完毕放行之后,才可以访问对应的资源。Filter过滤器一般用于完成一些通用的操作,比如:登录校验敏感字符处理等。

快速入门

Filter过滤器的快速入门十分简单,主要步骤分为两步:首先需要定义过滤器,定义一个类,实现Filter接口并重写其中所有方法;然后需要配置过滤器,在Filter类上添加@WebFilter注解,配置拦截资源的路径(只有请求对应的资源才会被过滤器拦截)。假如使用SpringBoot构建项目,还需要在引导类上添加@ServletComponentScan注解开启Servlet组件支持。

定义过滤器

定义Filter过滤器的代码:

public class DemoFilter implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {log.info("Init Filter");}@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {log.info("Filter 成功拦截请求");}@Overridepublic void destroy() {log.info("Destroy Filter");}
}

定义Filter过滤器的代码很简单,首先需要定义一个类实现Filter接口,然后重写Filter接口中的三个方法。

init方法

顾名思义,init方法Filter过滤器的初始化方法,在服务器启动时,会自动创建Filter过滤器对象,在创建过滤器对象的时候,就会自动调用init初始化方法初始化过滤器,并且这个方法只会被调用一次。

doFilter方法

doFilter方法是过滤器的核心部分,该方法在过滤器每一次拦截到了请求之后都会被调用,一般而言,这个方法会被调用多次,每一次拦截到请求,都会调用一次doFilter方法,而我们就可以在doFilter方法中实现一些功能,比如说对这次请求的JWT令牌进行解析,以此判断用户的登录状态。

destroy方法

destroy方法销毁过滤器的方法,当服务器关闭时,会自动调用destroy方法销毁过滤器,destroy方法也只会被调用一次。

配置过滤器

在定义Filter过滤器之后,过滤器并不会生效,此时还需要完成Filter过滤器的配置,过滤器的配置相对简单,只需要在Filter类上添加@WebFilter注解,并在其urlPatterns属性中指定需要拦截的请求路径。

@Slf4j
@WebFilter(urlPatterns = "/*") // 配置Filter过滤器的拦截请求路径,/*表示拦截所有
public class DemoFilter implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {log.info("Init Filter");}@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {log.info("Filter 成功拦截请求");}@Overridepublic void destroy() {log.info("Destroy Filter");}
}

通过/*通配符表示拦截所有请求(所有请求都会被过滤器所拦截,可以根据不同的需求更改路径),这样就完成了Filter过滤器的配置,当定义和配置都完成之后,Filter过滤器就可以真正开始工作了,此时启动服务器进行测试:

如图所示,在服务器启动的时候,自动创建Filter过滤器,并调用了其init方法初始化过滤器。此时向服务端发起请求: 

如图所示,发起的请求成功被Filter过滤器拦截,但是客户端并没有得到服务器响应的数据 

这是因为Filter过滤器,必须要执行放行操作,才可以访问到服务器中的资源,可以通过FilterChain实体类中的doFilter方法放行:

@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {log.info("Filter 成功拦截请求");filterChain.doFilter(servletRequest, servletResponse);
}

放行之后再发起请求,就可以成功访问到服务器中的资源了:

关闭服务器,发现Filter过滤器自动调用destroy方法销毁: 

登录校验过滤器

通过上文的快速入门,我们已经了解了如何创建并配置一个Filter过滤器,那么回归到主线,使用Filter过滤器完成登录校验功能。

实现逻辑

让我们回顾一下登录校验的实现逻辑:用户登录时需要访问登录接口login,当用户输入的用户名和密码验证成功之后,会生成一个JWT令牌,并且将JWT令牌返回给客户端,客户端会将JWT存储起来,然后在后续的每一次请求中,都携带这个JWT令牌到服务端,这个时候,就需要Filter过滤器进行工作了:在登录校验中,Filter过滤器必须要验证JWT令牌的有效性,如果令牌能够成功解析,则证明为有效令牌;反之则为无效令牌。若令牌有效,Filter过滤器则放行请求,让它访问服务端对应的资源;如果为无效令牌,则进行拦截,并给客户端响应一个错误的信息(401)。其流程如图所示:

这样,登录校验的实现逻辑已经比较清晰了,但需要注意一点:对于登录(login)请求,过滤器是不需要进行拦截的,因为用户发起登录请求,就是为了获得JWT令牌,其本身是没有JWT令牌的,如果进行拦截,那么永远都只会得到请求失败的结果

我们上述逻辑编写登录校验过滤器代码:

/*** 令牌校验过滤器(登录过滤器)*/
@Slf4j
@WebFilter(urlPatterns = "/*")
public class LoginFilter implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {log.info("Init Filter");}@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse;// 获取该请求的URLString url = httpServletRequest.getRequestURL().toString();// 判断该请求是否包含login,如果包含,说明该请求是登录,则放行if (url.contains("/login")) {log.info("login");filterChain.doFilter(httpServletRequest, httpServletResponse);return;}// 获取请求头中的token(JWT令牌)String jwt = httpServletRequest.getHeader("token");// 判断令牌是否存在,若不存在,则返回错误结果(未登录)if (jwt == null || jwt.isEmpty()) {log.info("JWT令牌为空,登录失败");httpServletResponse.setStatus(401);return;}// 解析JWT令牌,如果解析失败,则返回错误结果(未登录)try {JWTUtils.parseJWT(jwt);} catch (Exception e) {e.printStackTrace();log.info("JWT解析失败,登录失败");httpServletResponse.setStatus(401);return;}// 令牌解析成功,放行log.info("令牌解析成功,放行");filterChain.doFilter(httpServletRequest, httpServletResponse);}@Overridepublic void destroy() {log.info("Destroy Filter");}
}

我们配置的过滤器是会拦截所有的请求,但是这和我们分析的逻辑不一样,我们需要直接放行登录请求,所以说需要进行判断该请求是否是登录,可以根据请求路径进行判断。然后需要获取到这次请求的token,如果请求没有一个请求头token,那么可以判断该请求没有携带JWT令牌,表示该用户没有登录,所以说直接响应错误结果,无需进行判断最后才是解析token,假如在解析过程中报错,那么肯定意味着JWT令牌解析错误,意味着用户可能伪造或更改了令牌,也表示用户未登录,所以说不能放行,需要返回错误代码。如果成功解析JWT令牌,那么就可以调用FilterChain中的doFilter方法,放行该请求,让它访问服务端对应的资源。这就是 登录校验 的整个流程。此时让我们发起请求进行测试:

如图所示,发起了一个非登录的请求,由于此时还没有登录,所以说没有JWT令牌,所以说自然被Filter过滤器所拦截,请求失败。 

此时发起登录请求,由于我们的逻辑是登录请求直接放行,所以说这次请求就被Filter过滤器放行了,成功获得了JWT令牌。 

当我们登录后获得了JWT令牌,之后的请求就可以在请求头中携带JWT令牌发起请求,这样当我们的JWT令牌是正确的,被成功解析之后Filter过滤器就会放行,就可以访问到对应的资源了。

Filter过滤器使用细节

Filter过滤器执行流程

Filter过滤器执行流程如图所示:

当过滤器拦截到了请求之后,如果希望该请求访问到服务端资源,那么就需要执行doFilter方法进行放行,在doFilter方法所编写的代码就是放行前逻辑。但是值得注意的是——请求访问完资源之后,还会回到过滤器当中,如果有需求,回到过滤器之后还可以执行放行后逻辑,放行后逻辑就是doFilter方法之后的代码。

Filter过滤器拦截路径

可以根据不同的需求,配置不同的拦截路径,下面介绍几种常见的拦截路径:像/login这样的拦截路径属于具体拦截路径,只有请求路径为/login时,拦截器才会拦截;像/emps/*这样的拦截路径属于目录拦截,访问/emps下的所有资源的请求,都会被拦截;像/*这样的拦截路径十分简单粗暴,会拦截所有请求。可以根据不同的需求,灵活的配置拦截路径。

过滤器链

在一个web应用程序中,可以根据需求,配置多个拦截器,这样的多个拦截器就形成了一个过滤器链:

如图所示,在web服务端中,定义了两个Filter过滤器这两个过滤器就形成了一个过滤器链,过滤器链上的过滤器会一个一个的执行,先执行第一个;再执行第二个,必须要过滤器链上的最后一个过滤器放行后(也代表着所有过滤器都必须放行),请求才能访问到对应的资源。但是当请求返回时,会根据相反的顺序,通过过滤器链,先执行过滤器2;再执行过滤器1,最后给客户端响应数据。

并且,过滤器链的执行顺序,是根据过滤器名(字符串)的自然排序的,比如AbcFilter会先于DemoFilter执行。

总结

Filter过滤器Servlet中的技术,是用于统一拦截的,想要在web程序中使用过滤器,就需要定义一个过滤器,并且配置过滤器,定义其拦截路径,我们可以在Filter过滤器doFilter方法中编写一些逻辑决定该过滤器的放行逻辑,从而实现登录校验功能Spring框架中也提供了一个类似过滤器的Interceptor拦截器,且听下回分解。

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

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

相关文章

【R语言】R语言安装包的相关操作

一、管理R语言安装包 1、安装R包 install.packages() 2、查看已安装的R包 installed.packages() 3、更新R包 update.packages() 4、卸载R包 remove.packages() 二、加载R语言安装包 打开R语言时,基础包(base包)会自动被加载到内存中…

Vue指令v-on

目录 一、Vue中的v-on指令是什么?二、v-on指令的简写三、v-on指令的使用 一、Vue中的v-on指令是什么? v-on指令的作用是:为元素绑定事件。 二、v-on指令的简写 “v-on:“指令可以简写为”” 三、v-on指令的使用 1、v-on指令绑…

javaEE-8.JVM(八股文系列)

目录 一.简介 二.JVM中的内存划分 JVM的内存划分图: 堆区:​编辑 栈区:​编辑 程序计数器:​编辑 元数据区:​编辑 经典笔试题: 三,JVM的类加载机制 1.加载: 2.验证: 3.准备: 4.解析: 5.初始化: 双亲委派模型 概念: JVM的类加…

物业管理系统源码提升社区智能化管理效率与用户体验

内容概要 物业管理系统源码是一种针对社区管理需求而设计的软件解决方案,通过先进的智能化技术,使物业管理变得更加高效和人性化。随着城市化进程的加快,社区的管理复杂性不断增加,而这一系统的推出恰好为物业公司提供了极大的便…

读算法简史:从美索不达米亚到人工智能时代05天气预报

1. 天气预报 1.1. 自古以来,生命就与变幻莫测的天气息息相关 1.1.1. 在很多情况下,只要能提前一天得知天气情况,人类就可以避免灭顶之灾 1.1.2. 公元前2000年,准确预测天气是众神的特权 1.2. 大约在公元前650年,巴…

整形的存储形式和浮点型在计算机中的存储形式

在计算机科学的底层世界里,数据存储是基石般的存在。不同数据类型,如整形与浮点型,其存储方式犹如独特的密码,隐藏着计算机高效运行的秘密。理解它们,是深入掌握编程与计算机原理的关键。 一、整形的存储形式 原码、反…

Python网络自动化运维---批量登录设备

文章目录 目录 文章目录 前言 实验准备 一.批量登录 IP 连续的设备 1.1.1 实验代码 1.1.2 代码分段分解 1.1.3 实验结果验证 二.批量登录 IP 不连续的设备 2.2.1 实验代码 2.2.2 代码分段分解 2.2.3 实验结果验证 前言 在生产环境中,我们通常需要登录多个设备…

selenium记录Spiderbuf例题C03

防止自己遗忘,故作此为记录。 鸢尾花数据集(Iris Dataset) 这道题牵扯到JS动态加载。 步骤: (1)进入例题,需要找到按钮规律。 flip_xpath: str r"//li/a[onclickgetIrisData({});]" (2&…

【C++篇】位图与布隆过滤器

目录 一,位图 1.1,位图的概念 1.2,位图的设计与实现 1.5,位图的应用举例 1.4,位图常用应用场景 二,布隆过滤器 2.1,定义: 2.2,布隆过滤器的实现 2.3, 应…

基于SpringBoot的新闻资讯系统的设计与实现(源码+SQL脚本+LW+部署讲解等)

专注于大学生项目实战开发,讲解,毕业答疑辅导,欢迎高校老师/同行前辈交流合作✌。 技术范围:SpringBoot、Vue、SSM、HLMT、小程序、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容:…

Spring Boot 2 快速教程:WebFlux处理流程(五)

WebFlux请求处理流程 下面是spring mvc的请求处理流程 具体步骤: 第一步:发起请求到前端控制器(DispatcherServlet) 第二步:前端控制器请求HandlerMapping查找 Handler (可以根据xml配置、注解进行查找) 匹配条件包括…

C基础寒假练习(2)

一、输出3-100以内的完美数&#xff0c;(完美数&#xff1a;因子和(因子不包含自身)数本身 #include <stdio.h>// 函数声明 int isPerfectNumber(int num);int main() {printf("3-100以内的完美数有:\n");for (int i 3; i < 100; i){if (isPerfectNumber…

react-bn-面试

1.主要内容 工作台待办 实现思路&#xff1a; 1&#xff0c;待办list由后端返回&#xff0c;固定需要的字段有id(查详细)、type(本条待办的类型)&#xff0c;还可能需要时间&#xff0c;状态等 2&#xff0c;一个集中处理待办中转路由页&#xff0c;所有待办都跳转到这个页面…

GRN前沿:利用DigNet从scRNA-seq数据中生成基于扩散的基因调控网络

1.论文原名&#xff1a;Diffusion-based generation of gene regulatory network from scRNA-seq data with DigNet 2.出版时间&#xff1a;2024.12.18 3.doi: 10.1101/gr.279551.124 摘要&#xff1a; 基因调控网络&#xff08;GRN&#xff09;在细胞内基因的身份和功能之间…

AnswerRocket:通过 AI 辅助简化分析

AnswerRocket是一家专注于人工智能驱动数据分析和商业智能的领先企业&#xff0c;其核心产品是一款增强型分析平台&#xff0c;旨在通过自然语言处理&#xff08;NLP&#xff09;、机器学习&#xff08;ML&#xff09;和生成式AI技术&#xff0c;简化复杂数据的分析过程&#x…

小程序设计和开发:如何研究同类型小程序的优点和不足。

一、确定研究目标和范围 明确研究目的 在开始研究同类型小程序之前&#xff0c;首先需要明确研究的目的。是为了改进自己的小程序设计和开发&#xff0c;还是为了了解市场趋势和用户需求&#xff1f;不同的研究目的会影响研究的方法和重点。例如&#xff0c;如果研究目的是为了…

我的AI工具箱Tauri版-ZoomImageSDXL全图超清放大TILE+SDXL

本教程基于自研的AI工具箱Tauri版进行ComfyUI工作流ZoomImageSDXL全图超清放大TILESDXL。 ZoomImageSDXL全图超清放大TILESDXL 借助ControlNet的Tile技术与SDXL大模型&#xff0c;该工具能够在放大图像的同时&#xff0c;精准还原细节和纹理&#xff0c;确保输出效果既清晰锐利…

Java设计模式:行为型模式→状态模式

Java 状态模式详解 1. 定义 状态模式&#xff08;State Pattern&#xff09;是一种行为型设计模式&#xff0c;它允许对象在内部状态改变时改变其行为。状态模式通过将状态需要的行为封装在不同的状态类中&#xff0c;实现对象行为的动态改变。该模式的核心思想是分离不同状态…

蓝桥与力扣刷题(234 回文链表)

题目&#xff1a;给你一个单链表的头节点 head &#xff0c;请你判断该链表是否为回文链表。如果是&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 示例 1&#xff1a; 输入&#xff1a;head [1,2,2,1] 输出&#xff1a;true示例 2&#xff1a; 输入&…

【面经】字节南京一面部分题目记录

南京字节一面题&#xff0c;可能因为项目不太匹配&#xff0c;全程八股比较多&#xff0c;也有两道手撕代码题&#xff0c;强度还是有的。为了方便大家学习&#xff0c;大部分答案由GPT整理&#xff0c;有些题给出了我认为回答比较好的博客链接。 文章目录 一、python2 和 pyth…