Filter过滤器

1.Filter过滤器说明

  • 为什么需要过滤器?

    先来看一个例子:

    我们在登录网站页面时,需要先进行登录验证。

    用户访问的正常的流程应该是:

    1. 用户先通过登录页面进行验证,然后才可以访问各种页面。

    2. 为了防止用户绕过登录验证,我们需要在每个页面进行验证, 获取session,验证用户是否登录过。

      但是上述的方法又会产生下面的问题:

      1. 使用传统方法,每个页面都要进行登录验证
      2. 这将会造成代码的冗余,而且功能是重复的,比较麻烦,维护起来也不方便

      这时候就需要filter过滤器,它可以统一进行验证,比如权限,身份的验证,还可以进行日志记录,事务管理等...

  • 过滤器介绍

  1. Filter过滤器是JavaWeb的三大组件之一(Servlet程序,Listener监听器,Filter过滤器)
  2. Filter过滤器是JavaEE规范,是接口
  3. Filter过滤器的作用是:拦截请求,过滤响应
  4. 应用场景:
    • 权限检查
    • 日志操作
    • 事务管理

2.过滤器的基本原理

上述整个过程如下:

  1. 浏览器请求某个资源,Tomcat接收请求后,先判断是否需要使用过滤器。
  2. 在web.xml文件中配置过滤器,并指定过滤器的url-pattern(规则),tomcat会根据你指定的规则来决定是否需要使用过滤器。
  3. 如果需要,就使用过滤器,过滤器会根据你写的业务需要来进行验证。如果验证成功,就可以继续访问;如果验证失败,就返回业务指定的地方。
  4. 如果不需要过滤器,就直接访问/返回资源。

3.过滤器快速入门

需求:在web工程下,有后台管理目录manage,要求该目录下所有资源(html,图片,jsp,servlet)等资源需要用户登录后才能访问。

3.1思路分析

上述流程如下:

  1. 用户访问login.jsp页面填写数据,login.jsp将数据发送到loginCheckServlet验证。
  2. 如果验证成功,servlet创建用户浏览器关联的session并保存。
  3. servlet转发到过滤器,验证还是不是真的登录过(有无session)
    • 如果验证成功,就可以访问资源
    • 如果验证失败,就直接返回登录页面
  4. 如果用户直接访问资源,会先通过过滤器进行验证。
    • 如果没有登陆过,就打回登录页面,不允许未经登录就直接访问资源
    • 如果登录过,可以直接访问资源

3.2-1代码实现

  1. 首先创建项目,添加web支持,添加需要的jar包

  2. 完成模块的整体流程

    先完成一个正确完整的流程,再加入其它的功能,完善功能。

    总地来说就是先把框架搭建起来,再完善细节。

    2.1 先完整一个正确的流程-看到一个效果->后面代码就可以验证

    2.2 加入其它功能

    2.3 完善功能

  3. 配置Tomcat


login.jsp:

<%--
Created by IntelliJ IDEA.
User: li
Date: 2022/11/28
Time: 17:03
Version: 1.0
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>管理后台页面登录</title>
</head>
<body>
<h1>管理后台页面登录</h1>
<form action="<%=request.getContextPath()%>/loginCheckServlet" method="post">
u: <input type="text" name="username"/><br/><br/>
p: <input type="password" name="password"/><br/><br/>
<input type="submit" value="用户登录"/>
</form>
</body>
</html>

LoginCheckServlet:

package com.servlet;

import javax.servlet.*;
import javax.servlet.http.*;
import java.io.IOException; public class LoginCheckServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
} @Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//获取到用户名和密码(这里应到数据库获取)
// 这里假设密码是123456就可以通过
String username = request.getParameter("username");
String password = request.getParameter("password"); if ("123456".equals(password)) {
//合法,将用户名加入session
request.getSession().setAttribute("username", username);
//请求转发到admin.jsp
//根据过滤器原理:请求转发的链接路径是不会被过滤器拦截的,而重定向会被过滤器拦截
request.getRequestDispatcher("/manage/admin.jsp").forward(request, response);
} else {
//非法
//返回登录页面
request.getRequestDispatcher("/login.jsp").forward(request, response);
}
}
}

admin.jsp:

<%--
Created by IntelliJ IDEA.
User: li
Date: 2022/11/28
Time: 17:05
Version: 1.0
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>admin</title>
<%--指定浏览器解析的目录--%>
<base href="<%=request.getContextPath()%>/manage/">
</head>
<body>
<h1>后台管理</h1>
<a href="#">用户列表</a>||<a href="#">添加用户</a>||<a href="#">删除用户</a>
<hr>
<%--该图片放在manage目录下--%>
<img src="myphoto.png" height="300"/>
</body>
</html>

ManageFilter:

package com.filter;

import javax.servlet.*;
import java.io.IOException; public class ManageFilter implements Filter { @Override
public void init(FilterConfig filterConfig) throws ServletException {
//当Tomcat创建filter后,会调用该方法,进行初始化
System.out.println("ManageFilter init方法被调用...");
} @Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
//当每次调用该filter时,doFilter方法就会被调用
System.out.println("ManageFilter doFilter被调用...");
} @Override
public void destroy() {
//当filter对像被销毁时,就会调用该方法
System.out.println("ManageFilter destroy被调用...");
}
}

在web.xml配置Filter和Servlet:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!--filter一般写在其他servlet的前面-->
<!--
1.filter的配置和servlet非常相似,filter也是被Tomcat管理和维护的
(它的创建和销毁也是tomcat来处理的)
2.<url-pattern> 配置的是filter生效的规则。即当请求的url和其匹配的时候,会触发调用该filter
3. /manage/* 里面第一个斜杠/,会解析成http://ip:port/工程路径
4. 完整的路径就是 http://ip:port/工程路径/manage/*
意为当请求的url资源满足该条件时,都会调用该filter
-->
<filter>
<filter-name>ManageFilter</filter-name>
<filter-class>com.filter.ManageFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>ManageFilter</filter-name>
<url-pattern>/manage/*</url-pattern>
</filter-mapping> <!--配置servlet-->
<servlet>
<servlet-name>LoginCheckServlet</servlet-name>
<servlet-class>com.servlet.LoginCheckServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>LoginCheckServlet</servlet-name>
<url-pattern>/loginCheckServlet</url-pattern>
</servlet-mapping>
</web-app>

redeployTomcat,可以看到后台输出:

这说明filter对象是由Tomcat去创建的。我们先来看一下filter底层实现。

3.3filter底层原理分析

filter的xml配置和servlet非常相似,filter也是被Tomcat管理和维护的。我们之前在学习servlet的时候知道了Tomcat是如何管理维护servlet的:

详见:手动实现Tomcat底层机制+自己设计servlet->day03-实现02->3.4容器设计

同理,我们也可以这样理解:Tomcat还维护了两个关于filter的容器。一个容器filterURLMapping存放url-pattern和filter-name。另一个容器filterMapping存放filter-name和filter实例。

  • 一旦Tomcat启动以后:

    1. 把程序员配置的url和filter-name,放到另一容器filterURLMapping<String,String>中。
    2. 会将filter实例化,把filter-name和filter实例,放到filterMapping<String,Filter>容器里面。
  • Tomcat接收到请求时:获取请求中的url,和filterURLMapping容器中的url进行匹配。

    1. 如果匹配上了,在容器filterURLMapping中根据配置的url找到对应的filter-name
    2. 再到另一容器filterMapping中根据filter-name,找到对应filter实例,并调用实例,完成这个filter的doFilter方法。
  • 并且,有了filter机制后,在调用servlet前会先匹配filter

    • 根据request对象封装的uri,先到filterUrlMapping中匹配
    • 如果匹配上了,就走上诉流程,最后调用相应filter对象的doFilter方法
    • 如果没有匹配上,就直接走我们后面的servlet/jsp/html等。(至于是servlet,又会走它那一套机制)

3.2-2代码实现

回到之前的程序,我们在浏览器中直接访问manage目录下的资源admin.jsp,可以看到后台输出如下:


说明过滤器已经起作用了。

如果没有显示doFilter被调用,可能是因为浏览器缓存机制,建议在调试台点击禁用缓存。

现在来完善ManageFilter过滤器里面的业务代码:

既然请求manage目录下的资源首先要经过过滤器,那么我们可以在过滤器中获取session,如果发现session中已经保存了用户信息(即登录过),就放行;如果没有session信息,就返回登录页面。

package com.filter;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.io.IOException; public class ManageFilter implements Filter { @Override
public void init(FilterConfig filterConfig) throws ServletException {
//当Tomcat创建filter后,会调用该方法,进行初始化
System.out.println("ManageFilter init方法被调用...");
} @Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
FilterChain filterChain) throws IOException, ServletException {
//当每次调用该filter时,doFilter方法就会被调用
System.out.println("ManageFilter doFilter被调用...");
//如果这里没有调用继续请求的方法,就会停止在这 // 如果继续访问目标资源-->等价于放行
// 在调用过滤器前,request对象已经被创建并封装起来了
// 所以我们这里就可以通过servletRequest获取很多信息,比如访问url,session,比如访问的参数...
// 同时可以做事务管理,数据获取,日志管理等等 //获取session
//用户浏览器向服务器发送带有jsessionid的cookie,服务器根据该sid找到对应session
//这样写后面还可以接续使用httpServletRequest的相关方法
HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
HttpSession session = httpServletRequest.getSession();
//获取username session对象,后面还可以继续使用
Object username = session.getAttribute("username");
//如果在对应的session中,没有找到用户的用户名,就说明没有登录过,否则就是登录过
if (username != null) {
//用户登录成功过,直接放行
/**
* filterChain.doFilter(servletRequest, servletResponse);
* 1.doFilter代表继续访问目标资源
* 2.ServletRequest和 ServletResponse对象会传递给目标资源/文件
* 3.一定要理解filter传递的两个对象,在一次http请求中,
* 和后面的servlet/jsp中的request,response是同一个对象
*/
filterChain.doFilter(servletRequest, servletResponse);
} else {//说明没有登录过,令其返回登录页面
servletRequest.getRequestDispatcher("/login.jsp")
.forward(servletRequest, servletResponse);
} /**
* 关于chain.doFilter()方法:
* 在doFilter()方法中,在chain.doFilter()之前的代码,一般是对request执行的过滤操作;
* 在chain.doFilter()后面的代码,一般是对response执行的操作;
* chain.doFiter()执行下一个过滤器或者业务处理器。
* 如果在doFilter()方法中,不写chain.doFilter(),业务无法继续往下处理。
*/
} @Override
public void destroy() {
//当filter对像被销毁时,就会调用该方法
System.out.println("ManageFilter destroy被调用...");
}
}

3.4测试

  1. redeployTomcat,在浏览器访问login.jsp,因为login.jsp不在过滤器配置的url下,因此请求不会被拦截。

  2. 在login.jsp输入正确的用户数据,点击登录,可以看到后台成功转发到了manage目录下的admin.jsp。

    整个过程为:

    1. 用户在login.jsp页面输入数据,login.jsp将表单数据提交到loginCheckServlet中检查

    2. servlet发现数据合法,于是给浏览器返回一个jsessionid,并在服务器内存中创建和此jsessionid关联的session,然后在此session中放入用户数据,最后请求转发到/manage/admin.jsp。

    3. 根据filter原理,请求转发不会经过过滤器。但是在返回admin.jsp后,浏览器需要向服务器请求图片资源,并且,我们的图片资源刚好匹配在filter配置的url。因此关于图片的请求,将会经过过滤器。

      一次请求只能返回一个资源,想要获取图片资源,因此浏览器发出了第二次请求,去获取图片资源。


    综上所述,在这次过程中,只经过了一次过滤器,后台输出如下:

  3. 这时,打开浏览器新页面,访问http://localhost:8080/filter/manage/admin.jsp,可以看到访问成功,因为之前已经登录过了。

3.5补充说明

3.5.1关于filterChain.doFilter()

https://www.jb51.net/article/229734.htm

filterChain.doFilter(servletRequest, servletResponse);

chain.doFilter将请求转发给过滤器链下一个filter , 如果没有filter那就是你请求的资源

在doFilter()方法中,在chain.doFilter()之前的代码,一般是对request执行的过滤操作;在chain.doFilter()后面的代码,一般是对response执行的操作;chain.doFiter()执行下一个过滤器或者业务处理器。如果在doFilter()方法中,不写chain.doFilter(),业务无法继续往下处理;

  1. doFilter代表继续访问目标资源,如servlet,jsp,或是下一个filter

  2. ServletRequest和 ServletResponse对象会传递给目标资源/文件

  3. 在一次http请求中,filter传递的两个对象,和后面的servlet/jsp中的requestresponse是同一个对象

验证:filter传递的两个对象,在一次http请求中,与其后面的servlet/jsp中的request/response是同一对象

  1. 在ManageFilter中:

  2. 在admin.jsp中:

想法:我们直接访问web应用下的/manage/admin.jsp,因为该资源在filter的url规则下,因此当访问时,会调用过滤器,而过滤器中又将ServletRequest传递给admin.jsp。

我们同时在filter和admin.jsp中输出request,查看对象的hash值如何。

后台输出如下:两者的哈希值是一样的。

这说明在一个请求里面,过滤器里面的ServletRequest,Servletresponse,和后面目标资源拿到的request,response是同一个对象。是可以共用的。

3.5.2关于过滤器中的ServletRequest

HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;

我们将过滤器中的ServletRequest转为HttpServletRequest类型,由于HttpServletRequest中有很多方法,所以我们可以在过滤器中做一些日志记录。

day26-过滤器Filter的更多相关文章

  1. Angularjs在控制器(controller.js)的js代码中使用过滤器($filter)格式化日期/时间实例

    Angularjs内置的过滤器(filter)为我们的数据信息格式化提供了比较强大的功能,比如:格式化时间,日期.格式化数字精度.语言本地化.格式化货币等等.但这些过滤器一般都是在VIEW中使用的,比 ...

  2. 网站过滤器Filter

    实际上,Filter与Servlet及其相似,区别只是FIlter的doFilter()方法里多了一个FilterChain的参数,通过该参数可以控制是否放行用户的请求.网站有了过滤器十分的方便,可以 ...

  3. paip.输出内容替换在Apache 过滤器filter的设置

    paip.输出内容替换在Apache 过滤器filter的设置 作者Attilax  艾龙,  EMAIL:1466519819@qq.com 来源:attilax的专栏 地址:http://blog ...

  4. java Servlet中的过滤器Filter

    web.xml中元素执行的顺序listener->filter->struts拦截器->servlet. 1.过滤器的概念 Java中的Filter 并不是一个标准的Servlet ...

  5. [ionic开源项目教程] - 第6讲 过滤器filter的使用

    过滤器filter的使用 1.回顾 再熟悉一下tab1.html的代码: <div class="list"> <a ng-repeat="item i ...

  6. selvert的过滤器filter处理中文乱码

    注意问题:在学习用selvert的过滤器filter处理中文乱码时,在filter配置初始化时用了utf-8处理中文乱码,而在提交的jsp页面中却用了gbk.虽然两种都可以出来中文乱码,但是却造成了处 ...

  7. MVC之 自定义过滤器(Filter)

    MVC之 自定义过滤器(Filter) 一.自定义Filter 自定义Filter需要继承ActionFilterAttribute抽象类,重写其中需要的方法,来看下ActionFilterAttri ...

  8. Servlet中的过滤器Filter用法

    1.过滤器的概念 Java中的Filter 并不是一个标准的Servlet ,它不能处理用户请求,也不能对客户端生成响应. 主要用于对HttpServletRequest 进行预处理,也可以对Http ...

  9. Servlet中的过滤器Filter详解

    加载执行顺序 context-param->listener->filter->servlet web.xml中元素执行的顺序listener->filter->stru ...

  10. AngularJS的过滤器$filter

    过滤器(filter)主要用于数据的格式上,通过某个规则,把值处理后返回结果.例如获得数据集,可排序后再返回. ng内置的共有九种过滤器: currency 货币 使用currency可以将数字格式化 ...

随机推荐

  1. Linux配置系统yum源

    首先是需要你把需要使用的镜像挂载到系统上面,可以通过cd /dvd添加也可以直接上传到系统上 本文档是上传到系统上进行挂载 操作系统:Red Hat 7.6 挂载镜像:Red Hat 7.6 1.挂载 ...

  2. Java SE final关键字

    final关键字 final可以修饰类.属性.方法和局部变量 如下情况,可以使用final 当不希望类被继承时,可以用final修饰 当不希望父类的某个方法被子类覆盖/重写(override)时,可以 ...

  3. synchronized锁详解

    synchronized的意义 解决了Java共享内存模型带来的线程安全问题: 如:两个线程对初始值为 0 的静态变量一个做自增,一个做自减,各做 5000 次,结果是 0 吗?(针对这个问题进行分析 ...

  4. java~springboot(2022之后)~目录索引

    回到占占推荐博客索引 最近写了不过关于java,spring,微服务的相关文章,今天把它整理一下,方便大家学习与参考. java~springboot(2022之前)~目录索引 java~spring ...

  5. Prometheus与服务发现

    这种按需的资源使用方式对于监控系统而言就意味着没有了一个固定的监控目标,所有的监控对象(基础设施.应用.服务)都在动态的变化.对于Prometheus这一类基于Pull模式的监控系统,显然也无法继续使 ...

  6. 轻松绕过waf,内网技术,Cobalt Strike4.4远控木马绕waf流量监控

    DNS隧道技术可以解决运控木马无法上线的问题,waf,防火墙对tcp,http,https等端口有流量检测,这个时候我们就可以使用隧道技术,让cs木马走DNS隧道,不仅可以检测不到而且也是一种反溯源的 ...

  7. 将 N 叉树编码为二叉树

    将 N 叉树编码为二叉树 作者:Grey 原文地址: 博客园:将 N 叉树编码为二叉树 CSDN:将 N 叉树编码为二叉树 题目描述 将一棵n叉树编码为一棵二叉树,并对二叉树进行解码,得到原始的n叉树 ...

  8. Mybatis PageHelper 使用的注意事项

    什么时候会导致不安全的分页? PageHelper 方法使用了静态的 ThreadLocal 参数,分页参数和线程是绑定的. 只要你可以保证在 PageHelper 方法调用后紧跟 MyBatis 查 ...

  9. java.lang.ClassNotFoundException:(新建的servlet无法找到class文件)的报错

    该问题有可能是IDEA的部署没有更新的问题 将out中的 删除,然后重新导入即可

  10. LOJ2325「清华集训 2017」小Y和恐怖的奴隶主

    题目链接 首先dp很显然,\(f(i,s)\)表示到了第i轮,各种血量人数的情况为s今后的期望攻击boss次数.那么有\(f(i,s)=\frac{1}{num+1}*\sum_{s->s'}( ...