当前位置: 首页 > news >正文

登录认证(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拦截器,且听下回分解。

相关文章:

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

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

pytorch实现门控循环单元 (GRU)

人工智能例子汇总:AI常见的算法和例子-CSDN博客 特性GRULSTM计算效率更快,参数更少相对较慢,参数更多结构复杂度只有两个门(更新门和重置门)三个门(输入门、遗忘门、输出门)处理长时依赖一般适…...

Word List 2

词汇颜色标识解释 词汇表中的生词 词汇表中的词组成的搭配、派生词 例句中的生词 我自己写的生词(用于区分易混淆的词,无颜色标识) 不认识的单词或句式 单词的主要汉语意思 不太理解的句子语法和结构 Word List 2 英文音标中文regi…...

机器学习常用包numpy篇(四)函数运算

目录 前言 一、三角函数 二、双曲函数 三、数值修约 四、 求和、求积与差分 五、 指数与对数 六、算术运算 七、 矩阵与向量运算 八、代数运算 九、 其他数学工具 总结 前言 Python 的原生运算符可实现基础数学运算(加减乘除、取余、取整、幂运算&#…...

CSS in JS

css in js css in js 的核心思想是:用一个 JS 对象来描述样式,而不是 css 样式表。 例如下面的对象就是一个用于描述样式的对象: const styles {backgroundColor: "#f40",color: "#fff",width: "400px",he…...

TCP 丢包恢复策略:代价权衡与优化迷局

网络物理层丢包是一种需要偿还的债务,可以容忍低劣的传输质量,这为 UDP 类服务提供了空间,而对于 TCP 类服务,可以用另外两类代价来支付: 主机端采用轻率的 GBN 策略恢复丢包,节省 CPU 资源,但…...

面经--C语言——内存泄漏、malloc和new的区别 .c文件怎么转换为可执行程序 uart和usart的区别 继承的访问权限总结

文章目录 内存泄漏预防内存泄漏的方法: malloc和new的区别.c文件怎么转换为可执行程序uart和usart的区别继承的访问权限总结访问控制符总结1. **public**:2. **protected**:3. **private**:继承类型: 内存泄漏 内存泄漏是指程序在运行时动态分配内存后&…...

Denavit-Hartenberg DH MDH坐标系

Denavit-Hartenberg坐标系及其规则详解 6轴协作机器人的MDH模型详细图_6轴mdh-CSDN博客 N轴机械臂的MDH正向建模,及python算法_mdh建模-CSDN博客 运动学3-----正向运动学 | 鱼香ROS 机器人学:MDH建模 - 哆啦美 - 博客园 机械臂学习——标准DH法和改进MDH…...

力扣动态规划-20【算法学习day.114】

前言 ###我做这类文章一个重要的目的还是记录自己的学习过程,我的解析也不会做的非常详细,只会提供思路和一些关键点,力扣上的大佬们的题解质量是非常非常高滴!!! 习题 1.网格中的最小路径代价 题目链接…...

计算机视觉-边缘检测

一、边缘 1.1 边缘的类型 ①实体上的边缘 ②深度上的边缘 ③符号的边缘 ④阴影产生的边缘 不同任务关注的边缘不一样 1.2 提取边缘 突变-求导(求导也是一种卷积) 近似,1(右边的一个值-自己可以用卷积做) 该点f(x,y)…...

文字加持:让 OpenCV 轻松在图像中插上文字

前言 在很多图像处理任务中,我们不仅需要提取图像信息,还希望在图像上加上一些文字,或是标注,或是动态展示。正如在一幅画上添加一个标语,或者在一个视频上加上动态字幕,cv2.putText 就是这个“文字魔术师”,它能让我们的图像从“沉默寡言”变得生动有趣。 今天,我们…...

掌握 HTML5 多媒体标签:如何在所有浏览器中顺利嵌入视频与音频

系列文章目录 01-从零开始学 HTML:构建网页的基本框架与技巧 02-HTML常见文本标签解析:从基础到进阶的全面指南 03-HTML从入门到精通:链接与图像标签全解析 04-HTML 列表标签全解析:无序与有序列表的深度应用 05-HTML表格标签全面…...

在Mac mini M4上部署DeepSeek R1本地大模型

在Mac mini M4上部署DeepSeek R1本地大模型 安装ollama 本地部署,我们可以通过Ollama来进行安装 Ollama 官方版:【点击前往】 Web UI 控制端【点击安装】 如何在MacOS上更换Ollama的模型位置 默认安装时,OLLAMA_MODELS 位置在"~/.o…...

【电脑系统】电脑突然(蓝屏)卡死发出刺耳声音

文章目录 前言问题描述软件解决方案尝试硬件解决方案尝试参考文献 前言 在 更换硬盘 时遇到的问题,有时候只有卡死没有蓝屏 问题描述 更换硬盘后,电脑用一会就卡死,蓝屏,显示蓝屏代码 UNEXPECTED_STORE_EXCEPTION 软件解决方案…...

Docker使用指南(二)——容器相关操作详解(实战案例教学,创建/使用/停止/删除)

目录 1.容器操作相关命令​编辑 案例一: 案例二: 容器常用命令总结: 1.查看容器状态: 2.删除容器: 3.进入容器: 二、Docker基本操作——容器篇 1.容器操作相关命令 下面我们用两个案例来具体实操一…...

Java中的常见对象类型解析

在Java开发中,数据的组织和传递是一个重要的概念。为了确保代码的清晰性、可维护性和可扩展性,我们通常会根据不同的用途,设计和使用不同类型的对象。这些对象的作用各不相同,但它们共同为构建高效、模块化的软件架构提供支持。 …...

Dijkstra算法解析

Dijkstra算法,用于求解图中从一个起点到其他所有节点的最短路径。解决单源最短路径问题的有效方法。 条件 有向 带权路径 时间复杂度 O(n平方) 方法步骤 1 把图上的点分为两个集合 要求的起点 和除了起点之外的点 。能直达的写上权值 不…...

C++ Primer 多维数组

欢迎阅读我的 【CPrimer】专栏 专栏简介:本专栏主要面向C初学者,解释C的一些基本概念和基础语言特性,涉及C标准库的用法,面向对象特性,泛型特性高级用法。通过使用标准库中定义的抽象设施,使你更加适应高级…...

maven mysql jdk nvm node npm 环境安装

安装JDK 1.8 11 环境 maven环境安装 打开网站 下载 下载zip格式 解压 自己创建一个maven库 以后在idea 使用maven时候重新设置一下 这三个地方分别设置 这时候maven才算设置好 nvm 管理 npm nodejs nvm下载 安装 Releases coreybutler/nvm-windows GitHub 一键安装且若有…...

SQL Server中RANK()函数:处理并列排名与自然跳号

RANK()是SQL Server的窗口函数,为结果集中的行生成排名。当出现相同值时,后续排名会跳过被占用的名次,形成自然间隔。与DENSE_RANK()的关键区别在于是否允许排名值连续。 语法: RANK() OVER ([PARTITION BY 分组列]ORDER BY 排序…...

大型活动交通拥堵治理的视觉算法应用

大型活动下智慧交通的视觉分析应用 一、背景与挑战 大型活动(如演唱会、马拉松赛事、高考中考等)期间,城市交通面临瞬时人流车流激增、传统摄像头模糊、交通拥堵识别滞后等问题。以演唱会为例,暖城商圈曾因观众集中离场导致周边…...

Linux相关概念和易错知识点(42)(TCP的连接管理、可靠性、面临复杂网络的处理)

目录 1.TCP的连接管理机制(1)三次握手①握手过程②对握手过程的理解 (2)四次挥手(3)握手和挥手的触发(4)状态切换①挥手过程中状态的切换②握手过程中状态的切换 2.TCP的可靠性&…...

C++八股 —— 单例模式

文章目录 1. 基本概念2. 设计要点3. 实现方式4. 详解懒汉模式 1. 基本概念 线程安全(Thread Safety) 线程安全是指在多线程环境下,某个函数、类或代码片段能够被多个线程同时调用时,仍能保证数据的一致性和逻辑的正确性&#xf…...

ip子接口配置及删除

配置永久生效的子接口,2个IP 都可以登录你这一台服务器。重启不失效。 永久的 [应用] vi /etc/sysconfig/network-scripts/ifcfg-eth0修改文件内内容 TYPE"Ethernet" BOOTPROTO"none" NAME"eth0" DEVICE"eth0" ONBOOT&q…...

Spring AI Chat Memory 实战指南:Local 与 JDBC 存储集成

一个面向 Java 开发者的 Sring-Ai 示例工程项目,该项目是一个 Spring AI 快速入门的样例工程项目,旨在通过一些小的案例展示 Spring AI 框架的核心功能和使用方法。 项目采用模块化设计,每个模块都专注于特定的功能领域,便于学习和…...

Scrapy-Redis分布式爬虫架构的可扩展性与容错性增强:基于微服务与容器化的解决方案

在大数据时代,海量数据的采集与处理成为企业和研究机构获取信息的关键环节。Scrapy-Redis作为一种经典的分布式爬虫架构,在处理大规模数据抓取任务时展现出强大的能力。然而,随着业务规模的不断扩大和数据抓取需求的日益复杂,传统…...

DeepSeek越强,Kimi越慌?

被DeepSeek吊打的Kimi,还有多少人在用? 去年,月之暗面创始人杨植麟别提有多风光了。90后清华学霸,国产大模型六小虎之一,手握十几亿美金的融资。旗下的AI助手Kimi烧钱如流水,单月光是投流就花费2个亿。 疯…...

云原生安全实战:API网关Envoy的鉴权与限流详解

🔥「炎码工坊」技术弹药已装填! 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 一、基础概念 1. API网关 作为微服务架构的统一入口,负责路由转发、安全控制、流量管理等核心功能。 2. Envoy 由Lyft开源的高性能云原生…...

嵌入式面试常问问题

以下内容面向嵌入式/系统方向的初学者与面试备考者,全面梳理了以下几大板块,并在每个板块末尾列出常见的面试问答思路,帮助你既能夯实基础,又能应对面试挑战。 一、TCP/IP 协议 1.1 TCP/IP 五层模型概述 链路层(Link Layer) 包括网卡驱动、以太网、Wi‑Fi、PPP 等。负责…...

VSCode 使用CMake 构建 Qt 5 窗口程序

首先,目录结构如下图: 运行效果: cmake -B build cmake --build build 运行: windeployqt.exe F:\testQt5\build\Debug\app.exe main.cpp #include "mainwindow.h"#include <QAppli...