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

news2025/2/6 1:58:57

统一拦截

上文我们提到(登录认证(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 {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        log.info("Init Filter");
    }

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

    @Override
    public 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 {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        log.info("Init Filter");
    }

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

    @Override
    public 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 {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        log.info("Init Filter");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
        HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse;

        // 获取该请求的URL
        String 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);
    }

    @Override
    public 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.coloradmin.cn/o/2293542.html

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

相关文章

python算法和数据结构刷题[1]:数组、矩阵、字符串

一画图二伪代码三写代码 LeetCode必刷100题:一份来自面试官的算法地图(题解持续更新中)-CSDN博客 算法通关手册(LeetCode) | 算法通关手册(LeetCode) (itcharge.cn) 面试经典 150 题 - 学习计…

详解u3d之AssetBundle

一.AssetBundle的概念 “AssetBundle”可以指两种不同但相关的东西。 1.1 AssetBundle指的是u3d在磁盘上生成的存放资源的目录 目录包含两种类型文件(下文简称AB包): 一个序列化文件,其中包含分解为各个对象并写入此单个文件的资源。资源文件&#x…

接口测试通用测试用例

接口测试主要用于检测外部系统与系统之间以及内部各个子系统之间的交互点。 测试的重点是检查数据的交换,传递和控制管理过程,以及系统间的相互逻辑依赖关系等。 现在很多系统前后端架构是分离的,从安全层面来说,只依赖前段进行限…

【BUUCTF杂项题】荷兰宽带数据泄露、九连环

一.荷兰宽带数据泄露 打开发现是一个.bin为后缀的二进制文件,因为提示宽带数据泄露,考虑是宽带路由器方向的隐写 补充:大多数现代路由器都可以让您备份一个文件路由器的配置文件,软件RouterPassView可以读取这个路由配置文件。 用…

蓝桥杯思维训练营(三)

文章目录 题目详解680.验证回文串 II30.魔塔游戏徒步旅行中的补给问题观光景点组合得分问题 题目详解 680.验证回文串 II 680.验证回文串 II 思路分析:这个题目的关键就是,按照正常来判断对应位置是否相等,如果不相等,那么就判…

基于RTOS的STM32游戏机

1.游戏机的主要功能 所有游戏都来着B站JL单片机博主开源 这款游戏机具备存档与继续游戏功能,允许玩家在任何时候退出当前游戏并保存进度,以便日后随时并继续之前的冒险。不仅如此,游戏机还支持多任务处理,玩家可以在退出当前游戏…

计算机网络——三种交换技术

目录 电路交换——用于电话网络 电路交换的优点: 电路交换的缺点: 报文交换——用于电报网络 报文交换的优点: 报文交换的缺点: 分组交换——用于现代计算机网络 分组交换的优点: 分组交换的缺点 电路交换——…

HTML5教程之标签(2)

HTML5 <b> 标签 实例 在HTML5中&#xff0c;你可以使用<b>标签来对某些文本实现加粗的效果&#xff0c;请参考下述的示例&#xff1a; <p>这是一个普通的文本- <b>这是一个加粗文本</b>。</p> 尝试一下 浏览器支持 所有主流浏览器都支…

Verilog基础(一):基础元素

verilog基础 我先说,看了肯定会忘,但是重要的是这个过程,我们知道了概念,知道了以后在哪里查询。语法都是术,通用的概念是术。所以如果你有相关的软件编程经验,那么其实开启这个学习之旅,你会感受到熟悉,也会感受到别致。 入门 - 如何开始 欢迎来到二进制的世界,数字…

Vue 图片引用方式详解:静态资源与动态路径访问

目录 前言1. 引用 public/ 目录2. assets/ 目录3. 远程服务器4. Vue Router 动态访问5. 总结6. 扩展&#xff08;图片不显示&#xff09; 前言 &#x1f91f; 找工作&#xff0c;来万码优才&#xff1a;&#x1f449; #小程序://万码优才/r6rqmzDaXpYkJZF 在 Vue 开发中&#x…

Qt网络相关

“ 所有生而孤独的人&#xff0c;葆有的天真 ” 为了⽀持跨平台, QT对⽹络编程的 API 也进⾏了重新封装。本章会上手一套基于QT的网络通信编写。 UDP Socket 在使用Qt进行网络编程前&#xff0c;需要在Qt项目中的.pro文件里添加对应的网络模块( network ). QT core gui net…

生成式AI安全最佳实践 - 抵御OWASP Top 10攻击 (上)

今天小李哥将开启全新的技术分享系列&#xff0c;为大家介绍生成式AI的安全解决方案设计方法和最佳实践。近年来&#xff0c;生成式 AI 安全市场正迅速发展。据 IDC 预测&#xff0c;到 2025 年全球 AI 安全解决方案市场规模将突破 200 亿美元&#xff0c;年复合增长率超过 30%…

pytorch基于FastText实现词嵌入

FastText 是 Facebook AI Research 提出的 改进版 Word2Vec&#xff0c;可以&#xff1a; ✅ 利用 n-grams 处理未登录词 比 Word2Vec 更快、更准确 适用于中文等形态丰富的语言 完整的 PyTorch FastText 代码&#xff08;基于中文语料&#xff09;&#xff0c;包含&#xff1…

Docker技术相关学习三

一、Docker镜像仓库管理 1.docker仓库&#xff1a;用于存储和分发docker镜像的集中式存储库&#xff0c;开发者可以将自己创建的镜像推送到仓库中也可以从仓库中拉取所需要的镜像。 2.docker仓库&#xff1a; 公有仓库&#xff08;docker hub&#xff09;&#xff1a;任何人都可…

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

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

Python 网络爬虫实战:从基础到高级爬取技术

&#x1f4dd;个人主页&#x1f339;&#xff1a;一ge科研小菜鸡-CSDN博客 &#x1f339;&#x1f339;期待您的关注 &#x1f339;&#x1f339; 1. 引言 网络爬虫&#xff08;Web Scraping&#xff09;是一种自动化技术&#xff0c;利用程序从网页中提取数据&#xff0c;广泛…

python学opencv|读取图像(五十四)使用cv2.blur()函数实现图像像素均值处理

【1】引言 前序学习进程中&#xff0c;对图像的操作均基于各个像素点上的BGR值不同而展开。 对于彩色图像&#xff0c;每个像素点上的BGR值为三个整数&#xff0c;因为是三通道图像&#xff1b;对于灰度图像&#xff0c;各个像素上的BGR值是一个整数&#xff0c;因为这是单通…

控件【QT】

文章目录 控件QWidgetenabledgeometrysetGeometry qrcwindowOpacityQPixmapfonttoolTipfocusPolicystyleSheetQPushButtonRadio ButtionCheck Box显示类控件QProgressBarcalendarWidget 控件 Qt中已经提供了很多内置的控件了(按钮,文本框,单选按钮,复选按钮&#xff0c;下拉框…

STM32 串口发送与接收

接线图 代码配置 根据上一章发送的代码配置&#xff0c;在GPIO配置的基础上需要再配置PA10引脚做RX接收&#xff0c;引脚模式可以选择浮空输入或者上拉输入&#xff0c;在USART配置串口模式里加上RX模式。 配置中断 //配置中断 USART_ITConfig(USART1, USART_IT_RXNE, ENABLE…

【Unity2D 2022:UI】创建滚动视图

一、创建Scroll View游戏对象 在Canvas画布下新建Scroll View游戏对象 二、为Content游戏对象添加Grid Layout Group&#xff08;网格布局组&#xff09;组件 选中Content游戏物体&#xff0c;点击Add Competent添加组件&#xff0c;搜索Grid Layout Group组件 三、调整Grid La…