java中Filter的使用

1.1 Filter概述

Filter 表示过滤器,是 JavaWeb 三大组件(Servlet、Filter、Listener)之一。Servlet 我们之前都已经学习过了,Filter和Listener 我们今天都会进行学习。

过滤器可以把对资源的请求==拦截==下来,从而实现一些特殊的功能。

如下图所示,浏览器可以访问服务器上的所有的资源(servlet、jsp、html等)

而在访问到这些资源之前可以使过滤器拦截来下,也就是说在访问资源之前会先经过 Filter,如下图

拦截器拦截到后可以做什么功能呢?

==过滤器一般完成一些通用的操作。==比如每个资源都要写一些代码完成某个功能,我们总不能在每个资源中写这样的代码吧,而此时我们可以将这些代码写在过滤器中,因为请求每一个资源都要经过过滤器。

我们之前做的品牌数据管理的案例中就已经做了登陆的功能,而如果我们不登录能不能访问到数据呢?我们可以在浏览器直接访问首页 ,可以看到 查询所有 的超链接

当我点击该按钮,居然可以看到品牌的数据

这显然和我们的要求不符。我们希望实现的效果是用户如果登陆过了就跳转到品牌数据展示的页面;如果没有登陆就跳转到登陆页面让用户进行登陆,要实现这个效果需要在每一个资源中都写上这段逻辑,而像这种通用的操作,我们就可以放在过滤器中进行实现。这个就是==权限控制==,以后我们还会进行细粒度权限控制。过滤器还可以做 统一编码处理敏感字符处理 等等…

1.2 Filter快速入门

1.2.1 开发步骤

进行 Filter 开发分成以下三步实现

  • 定义类,实现 Filter接口,并重写其所有方法

  • 配置Filter拦截资源的路径:在类上定义 @WebFilter 注解。而注解的 value 属性值 /* 表示拦截所有的资源

  • 在doFilter方法中输出一句话,并放行

    上述代码中的 chain.doFilter(request,response); 就是放行,也就是让其访问本该访问的资源。

1.2.2 代码演示

创建一个项目,项目下有一个 hello.jsp 页面,项目结构如下:

pom.xml 配置文件内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion>
​<groupId>org.example</groupId><artifactId>filter-demo</artifactId><version>1.0-SNAPSHOT</version><packaging>war</packaging>
​<properties><maven.compiler.source>8</maven.compiler.source><maven.compiler.target>8</maven.compiler.target></properties>
​<dependencies><dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId><version>3.1.0</version><scope>provided</scope></dependency></dependencies>
​<build><plugins><plugin><groupId>org.apache.tomcat.maven</groupId><artifactId>tomcat7-maven-plugin</artifactId><version>2.2</version><configuration><port>80</port></configuration></plugin></plugins></build>
</project>

hello.jsp 页面内容如下:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>Title</title>
</head>
<body><h1>hello JSP~</h1>
</body>
</html>

我们现在在浏览器输入 http://localhost/filter-demo/hello.jsp 访问 hello.jsp 页面,这里是可以访问到 hello.jsp 页面内容的。

接下来编写过滤器。过滤器是 Web 三大组件之一,所以我们将 filter 创建在 com.itheima.web.filter 包下,起名为 FilterDemo

@WebFilter("/*")
public class FilterDemo implements Filter {
​@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {System.out.println("FilterDemo...");}
​@Overridepublic void init(FilterConfig filterConfig) throws ServletException {}
​@Overridepublic void destroy() {}
}

重启启动服务器,再次重新访问 hello.jsp 页面,这次发现页面没有任何效果,但是在 idea 的控制台可以看到如下内容

上述效果说明 FilterDemo 这个过滤器的 doFilter() 方法执行了,但是为什么在浏览器上看不到 hello.jsp 页面的内容呢?这是因为在 doFilter() 方法中添加放行的方法才能访问到 hello.jsp 页面。那就在 doFilter() 方法中添加放行的代码

//放行chain.doFilter(request,response);

再次重启服务器并访问 hello.jsp 页面,发现这次就可以在浏览器上看到页面效果。

FilterDemo 过滤器完整代码如下:

@WebFilter("/*")
public class FilterDemo implements Filter {
​@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {System.out.println("1.FilterDemo...");//放行chain.doFilter(request,response);}
​@Overridepublic void init(FilterConfig filterConfig) throws ServletException {}
​@Overridepublic void destroy() {}
}
​

1.3 Filter执行流程

如上图是使用过滤器的流程,我们通过以下问题来研究过滤器的执行流程:

  • 放行后访问对应资源,资源访问完成后,还会回到Filter中吗?

    从上图就可以看出肯定 ==会== 回到Filter中

  • 如果回到Filter中,是重头执行还是执行放行后的逻辑呢?

    如果是重头执行的话,就意味着 放行前逻辑 会被执行两次,肯定不会这样设计了;所以访问完资源后,会回到 放行后逻辑,执行该部分代码。

通过上述的说明,我们就可以总结Filter的执行流程如下:

接下来我们通过代码验证一下,在 doFilter() 方法前后都加上输出语句,如下

同时在 hello.jsp 页面加上输出语句,如下

执行访问该资源打印的顺序是按照我们标记的标号进行打印的话,说明我们上边总结出来的流程是没有问题的。启动服务器访问 hello.jsp 页面,在控制台打印的内容如下:

以后我们可以将对请求进行处理的代码放在放行之前进行处理,而如果请求完资源后还要对响应的数据进行处理时可以在放行后进行逻辑处理。

1.4 Filter拦截路径配置

拦截路径表示 Filter 会对请求的哪些资源进行拦截,使用 @WebFilter 注解进行配置。如:@WebFilter("拦截路径")

拦截路径有如下四种配置方式:

  • 拦截具体的资源:/index.jsp:只有访问index.jsp时才会被拦截

  • 目录拦截:/user/*:访问/user下的所有资源,都会被拦截

  • 后缀名拦截:*.jsp:访问后缀名为jsp的资源,都会被拦截

  • 拦截所有:/*:访问所有资源,都会被拦截

通过上面拦截路径的学习,大家会发现拦截路径的配置方式和 Servlet 的请求资源路径配置方式一样,但是表示的含义不同。

1.5 过滤器链

1.5.1 概述

过滤器链是指在一个Web应用,可以配置多个过滤器,这多个过滤器称为过滤器链。

如下图就是一个过滤器链,我们学习过滤器链主要是学习过滤器链执行的流程

上图中的过滤器链执行是按照以下流程执行:

  1. 执行 Filter1 的放行前逻辑代码

  2. 执行 Filter1 的放行代码

  3. 执行 Filter2 的放行前逻辑代码

  4. 执行 Filter2 的放行代码

  5. 访问到资源

  6. 执行 Filter2 的放行后逻辑代码

  7. 执行 Filter1 的放行后逻辑代码

以上流程串起来就像一条链子,故称之为过滤器链。

1.5.2 代码演示
  • 编写第一个过滤器 FilterDemo ,配置成拦截所有资源

    @WebFilter("/*")
    public class FilterDemo implements Filter {
    ​@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
    ​//1. 放行前,对 request数据进行处理System.out.println("1.FilterDemo...");//放行chain.doFilter(request,response);//2. 放行后,对Response 数据进行处理System.out.println("3.FilterDemo...");}
    ​@Overridepublic void init(FilterConfig filterConfig) throws ServletException {}
    ​@Overridepublic void destroy() {}
    }

  • 编写第二个过滤器 FilterDemo2 ,配置炒年糕拦截所有资源

    @WebFilter("/*")
    public class FilterDemo2 implements Filter {
    ​@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
    ​//1. 放行前,对 request数据进行处理System.out.println("2.FilterDemo...");//放行chain.doFilter(request,response);//2. 放行后,对Response 数据进行处理System.out.println("4.FilterDemo...");}
    ​@Overridepublic void init(FilterConfig filterConfig) throws ServletException {}
    ​@Overridepublic void destroy() {}
    }
  • 修改 hello.jsp 页面中脚本的输出语句

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head><title>Title</title>
    </head>
    <body><h1>hello JSP~</h1><%System.out.println("3.hello jsp");%>
    </body>
    </html>

  • 启动服务器,在浏览器输入 http://localhost/filter-demo/hello.jsp 进行测试,在控制台打印内容如下

    从结果可以看到确实是按照我们之前说的执行流程进行执行的。

1.5.3 问题

上面代码中为什么是先执行 FilterDemo ,后执行 FilterDemo2 呢?

我们现在使用的是注解配置Filter,而这种配置方式的优先级是按照过滤器类名(字符串)的自然排序。

比如有如下两个名称的过滤器 : BFilterDemoAFilterDemo 。那一定是 AFilterDemo 过滤器先执行。

1.6 案例

1.6.1 需求

访问服务器资源时,需要先进行登录验证,如果没有登录,则自动跳转到登录页面

1.6.2 分析

我们要实现该功能是在每一个资源里加入登陆状态校验的代码吗?显然是不需要的,只需要写一个 Filter ,在该过滤器中进行登陆状态校验即可。而在该 Filter 中逻辑如下:

1.6.3 代码实现
1.6.3.1 创建Filter

brand-demo 工程创建 com.itheima.web.filter 包,在该下创建名为 LoginFilter 的过滤器

@WebFilter("/*")
public class LoginFilter implements Filter {@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {}
​public void init(FilterConfig config) throws ServletException {}
​public void destroy() {}
}
1.6.3.2 编写逻辑代码

doFilter() 方法中编写登陆状态校验的逻辑代码。

我们首先需要从 session 对象中获取用户信息,但是 ServletRequest 类型的 requset 对象没有获取 session 对象的方法,所以此时需要将 request对象强转成 HttpServletRequest 对象。

HttpServletRequest req = (HttpServletRequest) request;

然后完成以下逻辑

  • 获取Session对象

  • 从Session对象中获取名为 user 的数据

  • 判断获取到的数据是否是 null

    • 如果不是,说明已经登陆,放行

    • 如果是,说明尚未登陆,将提示信息存储到域对象中并跳转到登陆页面

代码如下:

@WebFilter("/*")
public class LoginFilter implements Filter {@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {HttpServletRequest req = (HttpServletRequest) request;//1. 判断session中是否有userHttpSession session = req.getSession();Object user = session.getAttribute("user");
​//2. 判断user是否为nullif(user != null){// 登录过了//放行chain.doFilter(request, response);}else {// 没有登陆,存储提示信息,跳转到登录页面
​req.setAttribute("login_msg","您尚未登陆!");req.getRequestDispatcher("/login.jsp").forward(req,response);}}
​public void init(FilterConfig config) throws ServletException {}
​public void destroy() {}
}
1.6.3.3 测试并抛出问题

在浏览器上输入 http://localhost:8080/brand-demo/ ,可以看到如下页面效果

从上面效果可以看出没有登陆确实是跳转到登陆页面了,但是登陆页面为什么展示成这种效果了呢?

1.6.3.4 问题分析及解决

因为登陆页面需要 css/login.css 这个文件进行样式的渲染,下图是登陆页面引入的css文件图解

而在请求这个css资源时被过滤器拦截,就相当于没有加载到样式文件导致的。解决这个问题,只需要对所以的登陆相关的资源进行放行即可。还有一种情况就是当我没有用户信息时需要进行注册,而注册时也希望被过滤器放行。

综上,我们需要在判断session中是否包含用户信息之前,应该加上对登陆及注册相关资源放行的逻辑处理

//判断访问资源路径是否和登录注册相关
//1,在数组中存储登陆和注册相关的资源路径
String[] urls = {"/login.jsp","/imgs/","/css/","/loginServlet","/register.jsp","/registerServlet","/checkCodeServlet"};
//2,获取当前访问的资源路径
String url = req.getRequestURL().toString(); 
​
//3,遍历数组,获取到每一个需要放行的资源路径
for (String u : urls) {//4,判断当前访问的资源路径字符串是否包含要放行的的资源路径字符串/*比如当前访问的资源路径是  /brand-demo/login.jsp而字符串 /brand-demo/login.jsp 包含了  字符串 /login.jsp ,所以这个字符串就需要放行*/if(url.contains(u)){//找到了,放行chain.doFilter(request, response);//break;return;}
}
1.6.3.5 过滤器完整代码
@WebFilter("/*")
public class LoginFilter implements Filter {@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {HttpServletRequest req = (HttpServletRequest) request;//判断访问资源路径是否和登录注册相关//1,在数组中存储登陆和注册相关的资源路径String[] urls = {"/login.jsp","/imgs/","/css/","/loginServlet","/register.jsp","/registerServlet","/checkCodeServlet"};//2,获取当前访问的资源路径String url = req.getRequestURL().toString(); 
​//3,遍历数组,获取到每一个需要放行的资源路径for (String u : urls) {//4,判断当前访问的资源路径字符串是否包含要放行的的资源路径字符串/*比如当前访问的资源路径是  /brand-demo/login.jsp而字符串 /brand-demo/login.jsp 包含了  字符串 /login.jsp ,所以这个字符串就需要放行*/if(url.contains(u)){//找到了,放行chain.doFilter(request, response);//break;return;}}//1. 判断session中是否有userHttpSession session = req.getSession();Object user = session.getAttribute("user");
​//2. 判断user是否为nullif(user != null){// 登录过了//放行chain.doFilter(request, response);}else {// 没有登陆,存储提示信息,跳转到登录页面
​req.setAttribute("login_msg","您尚未登陆!");req.getRequestDispatcher("/login.jsp").forward(req,response);}}
​public void init(FilterConfig config) throws ServletException {}
​public void destroy() {}
}

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

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

相关文章

Python入门到精通(六)——Python函数进阶

Python函数进阶 一、函数的多返回值 二、函数多种传参方式 1、位置参数 2、关键字参数 3、缺省参数 4、不定长参数 &#xff08;1&#xff09;位置传递 &#xff08;2&#xff09;关键字传递 三、匿名函数 &#xff08;1&#xff09;函数作为参数传递 &#xff08;2&…

通过Demo学WPF—数据绑定(一)✨

前言✨ 想学习WPF&#xff0c;但是看视频教程觉得太耗时间&#xff0c;直接看文档又觉得似懂非懂&#xff0c;因此想通过看Demo代码文档的方式进行学习。 准备✨ 微软官方其实提供了WPF的一些Demo&#xff0c;地址为&#xff1a;microsoft/WPF-Samples: Repository for WPF …

day25 日期对象

日期对象 日期对象&#xff1a;用来表示时间的对象作用&#xff1a;可以得到当前系统时间 实例化 在代码中发现了new关键字时&#xff0c;一般将这个操作称为实例化创建一个时间对象并获取时间&#xff08;可以用于计算倒计时&#xff09; 获得当前时间const date new Date…

使用docker部署mongodb

1.创建目录 mkdir -p /opt/mongodb/{data,logs,config} 2.创建配置文件 进入目录 cd /opt写入配置 vim mongod.conf 内容如下 systemLog:# MongoDB发送所有日志输出的目标指定为文件destination: file# mongod或mongos应向其发送所有诊断日志记录信息的日志文件的路径path:…

QT 使用XML保存操作记录

文章目录 1 实现程序保存操作记录的思路2 XML文档基本结构3 QDomDocument实现XML读写3.1 QDomDocument实现生成XML文件3.2 QDomDocument实现读取XML文件 4 QXmlStreamWriter实现读写4.1 QXmlStreamWriter实现生成XML4.2 QXmlStreamWriter实现读取XML 1 实现程序保存操作记录的思…

【linux】-centos7版本前后-变化篇

1.centos7版本前后区别 首先文件系统变化&#xff0c;由EXT4&#xff0c;变为XFS格式。可支持容量500TB的文件&#xff0c;而6代仅能支持16TB。首个进程变为systemd, 替换了熟悉的init进程。它的特点是功能强大&#xff0c;体积也很强大。 systemd给我们带来了一个全家桶命令&…

【linux】磁盘空间不足-常用排查和处理命令

【linux】磁盘空间不足-常用排查和处理命令 1.通查一下 df -h #查看服务器磁盘空间情况 du -hs * 2>/dev/null #列出各目录所占空间大小 或 du -h -d 1 2>/dev/null #列出各目录所占空间大小 1.1情况一 df 磁盘空间和du 目录空间占用相等&#xff0c…

python17-Python的字符串格式化

Python提供了“%”对各种类型的数据进行格式化输出,例如如下代码。 # !/usr/bin/env python# -*- coding: utf-8 -*-# @Time : 2024/01# @Author : Laopiweight = 180print(老师傅的体重是 %s % weight) 上面程序就是格式化输出的关键代码,这行代码中的 print 函数包含三个部…

C# 二分搜索(Binary Search)

二分搜索概念 二分查找也称折半查找&#xff08;Binary Search&#xff09;它是一种效率较高的查找方法。但是&#xff0c;折半查找要求线性表必须采用顺序存储结构&#xff0c;而且表中元素按关键字有序排列。 二分搜索的背景 二分搜索法的概念和思想可以追溯到古代的中国和…

【python】求矩阵的特征值和特征向量

使用np.linalg.eig同时求特征值和特征向量 import numpy as np#输入矩阵 A np.array([[1, 1/2, 1/6, 1/9],[2, 1, 1/3, 1/5],[6, 3,1,1/2],[9, 5,2,1]])#求解特征值和其对应的特征向量 eigval,eigvec np.linalg.eig(A) for i in range(len(eigval)):print(f特征值&#xff1a…

STM32学习笔记一——初识STM32

目录 一、什么是ARM 二. Cortex 内核 三.什么是STM32 四.STM32核心板原理图&#xff1a; 五.STM32的内部结构&#xff1a; 六.stm32系统结构简化图 STM32基本原理分析&#xff1a; 七.典型型号——STM32F103ZET6 stm32——32位单片机&#xff08;数据总线是32位的&am…

mysql学习打卡day19

今日成果&#xff1a; update invoices set payment_total invoice_total *0.5,payment_date due_date where invoice_id 1; -- 更新单行记录 update orders set comments 金牌顾客 where customer_id in (select customer_id from customers where points > 3000); --…

文献速递:人工智能医学影像分割--- 深度学习分割骨盆骨骼:大规模CT数据集和基线模型

文献速递&#xff1a;人工智能医学影像分割— 深度学习分割骨盆骨骼&#xff1a;大规模CT数据集和基线模型 我们为大家带来人工智能技术在医学影像分割上的应用文献。 人工智能在医学影像分析中发挥着至关重要的作用&#xff0c;尤其体现在图像分割技术上。这项技术的目的是准…

如何将抖音API应用于抖音视频的录制和上传

抖音API允许开发者进行二次开发&#xff0c;使得第三方应用程序可以与抖音进行交互。要将抖音API应用于抖音视频的录制和上传&#xff0c;你需要遵循以下步骤&#xff1a; 获取抖音API密钥&#xff1a;首先&#xff0c;你需要从抖音官网注册一个开发者账号&#xff0c;并创建一…

Golang 流媒体服务器lalserver使用指南

目录 安装 使用 1.推流 2.播放 官方地址 安装 1.下载源码 wget https://github.com/q191201771/lal/releases/download/v0.36.7/lal_v0.36.7_linux.zipunzip lal_v0.36.7_linux.zip cd lal_v0.36.7_linux 2.启动 ./bin/lalserver -c ./conf/lalserver.conf.json 使用 …

TDengine 签约海博思创,助力储能运维平台数据管理

随着储能产业步入快速发展期&#xff0c;各类储能电站快速建设投产&#xff0c;规模各异&#xff0c;场景不同。为了实现储能电站的高效监控和运维管理&#xff0c;储能运维管理平台成为不可或缺的工具。通过高效的集中控制手段&#xff0c;这些平台能够有效解决储能电站运维成…

浅谈直流电表在韩国充电桩生产厂家的应用

I.背景&#xff1a; 近几年为应对温室气体的排放导致的全球变暖、气候变化等问题,各大国纷纷对焦推进电动汽车&#xff0c;从而减少传统燃油汽车带来的大量温室气体排放。而推进新能源汽车的各项举措之中&#xff0c;充电桩的基础建设&#xff0c;又是其中的重中之重&#xff…

AR眼镜_ar智能眼镜显示方案|光学方案

AR眼镜是一种智能眼镜&#xff0c;能够将虚拟现实和现实世界相结合&#xff0c;使人们能够在日常生活中体验和参与虚拟现实。然而&#xff0c;AR智能眼镜的制造成本高&#xff0c;开发周期长。要实现AR眼镜的各项功能&#xff0c;需要良好的硬件条件&#xff0c;而AR智能眼镜的…

python18-Python的字符串序列相关方法

字符串本质上就是由多个字符组成的,因此程序允许通过索引来操作字符,比如获取指定索引处的字符,获取指定字符在字符串中的位置等。 Python字符串直接在方括号([])中使用索引即可获取对应的字符,字符串中第一个字符的索引为0、第二个字符的索引为1,后面各字符依此类推。 …

数据库管理-第138期 数据库国产化是Office换WPS么(20240124)

数据库管理138期 2024-01-24 第138期 数据库国产化是Office换WPS么&#xff08;20240124&#xff09;1 背景2 现实3 方法论总结 第138期 数据库国产化是Office换WPS么&#xff08;20240124&#xff09; 作者&#xff1a;胖头鱼的鱼缸&#xff08;尹海文&#xff09; Oracle ACE…