Filter拦截器
1 Filter快速入门
1.1 开发步骤
进行 Filter
开发分成以下三步实现
- 定义类,实现 Filter接口,并重写其所有方法
-
配置Filter拦截资源的路径:在类上定义
@WebFilter
注解。而注解的value
属性值/*
表示拦截所有的资源
-
在doFilter方法中输出一句话,并放行
上述代码中的
chain.doFilter(request,response);
就是放行,也就是让其访问本该访问的资源。
1.2 代码演示
创建一个项目,项目下有一个 hello.jsp
页面:
hello.jsp
页面内容如下:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>hello JSP~</h1>
</body>
</html>
pom.xml
配置文件内容如下:
<?xml version="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>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.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>
我们现在在浏览器输入 http://localhost/filter-demo/hello.jsp
访问 hello.jsp
页面,这里是可以访问到 hello.jsp
页面内容的。
接下来编写过滤器。过滤器是 Web 三大组件之一,接下来创建Filter
类,起名为 FilterDemo
@WebFilter("/*")
public class FilterDemo implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
System.out.println("FilterDemo...");
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public 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 {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
System.out.println("FilterDemo...");
//放行
chain.doFilter(request,response);
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void destroy() {
}
}
2 Filter执行流程
过滤器的流程:
总结Filter的执行流程如下:
接下来我们通过代码验证一下,在 doFilter()
方法前后都加上输出语句,如下
同时在 hello.jsp
页面加上输出语句,如下
执行访问该资源打印的顺序是按照我们标记的标号进行打印的话,总结出来的流程是没有问题的。启动服务器访问 hello.jsp
页面,在控制台打印的内容如下:
以后我们可以将对请求进行处理的代码放在放行之前进行处理,而如果请求完资源后还要对响应的数据进行处理时可以在放行后进行逻辑处理。
3 Filter拦截路径配置
拦截路径表示 Filter 会对请求的哪些资源进行拦截,使用 @WebFilter
注解进行配置。如:@WebFilter("拦截路径")
拦截路径有如下四种配置方式:
- 拦截具体的资源:
/index.jsp
:只有访问index.jsp时才会被拦截 - 目录拦截:
/user/*
:访问/user下的所有资源,都会被拦截 - 后缀名拦截:
*.jsp
:访问后缀名为jsp的资源,都会被拦截 - 拦截所有:
/*
:访问所有资源,都会被拦截
4 过滤器链
4.1 概述
过滤器链是指在一个Web应用,可以配置多个过滤器,这多个过滤器称为过滤器链。
如下图就是一个过滤器链
上图中的过滤器链执行是按照以下流程执行:
- 执行
Filter1
的放行前逻辑代码 - 执行
Filter1
的放行代码 - 执行
Filter2
的放行前逻辑代码 - 执行
Filter2
的放行代码 - 访问到资源
- 执行
Filter2
的放行后逻辑代码 - 执行
Filter1
的放行后逻辑代码
以上流程串起来就像一条链子,故称之为过滤器链。
4.2 代码演示
-
编写第一个过滤器
FilterDemo
,配置成拦截所有资源@WebFilter("/*") public class FilterDemo implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { // 放行前,对 request数据进行处理 System.out.println("FilterDemo..."); //放行 chain.doFilter(request,response); //2. 放行后,对Response 数据进行处理 System.out.println("3.FilterDemo..."); } @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void destroy() { } }
-
编写第二个过滤器
FilterDemo2
,配置炒年糕拦截所有资源@WebFilter("/*") public class FilterDemo2 implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { // 放行前,对 request数据进行处理 System.out.println("2.FilterDemo..."); //放行 chain.doFilter(request,response); //2. 放行后,对Response 数据进行处理 System.out.println("4.FilterDemo..."); } @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public 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
进行测试,在控制台打印内容如下
5 案例
5.1 需求
访问服务器资源时,需要先进行登录验证,如果没有登录,则自动跳转到登录页面
5.2 分析
我们要实现该功能是在每一个资源里加入登陆状态校验的代码吗?显然是不需要的,只需要写一个 Filter
,在该过滤器中进行登陆状态校验即可。而在该 Filter
中逻辑如下:
5.3 代码实现
5.3.1 创建Filter
在 brand-demo
工程创建 web.filter
包,在该下创建名为 LoginFilter
的过滤器
@WebFilter("/*")
public class LoginFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
}
public void init(FilterConfig config) throws ServletException {
}
public void destroy() {
}
}
5.3.2 编写逻辑代码
在 doFilter()
方法中编写登陆状态校验的逻辑代码。
我们首先需要从 session
对象中获取用户信息,但是 ServletRequest
类型的 requset 对象没有获取 session 对象的方法,所以此时需要将 request对象强转成 HttpServletRequest
对象。
HttpServletRequest req = (HttpServletRequest) request;
然后完成以下逻辑
- 获取Session对象
- 从Session对象中获取名为
user
的数据 - 判断获取到的数据是否是 null
- 如果不是,说明已经登陆,放行
- 如果是,说明尚未登陆,将提示信息存储到域对象中并跳转到登陆页面
代码如下:
@WebFilter("/*")
public class LoginFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
HttpServletRequest req = (HttpServletRequest) request;
// 判断session中是否有user
HttpSession session = req.getSession();
Object user = session.getAttribute("user");
//2. 判断user是否为null
if(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() {
}
}
5.3.3 测试并抛出问题
在浏览器上输入 http://localhost:8080/brand-demo/
,可以看到如下页面效果
从上面效果可以看出没有登陆确实是跳转到登陆页面了,但是登陆页面为什么展示成这种效果了呢?
5.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;
}
}
5.3.5 过滤器完整代码
@WebFilter("/*")
public class LoginFilter implements Filter {
@Override
public 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;
}
}
// 判断session中是否有user
HttpSession session = req.getSession();
Object user = session.getAttribute("user");
//2. 判断user是否为null
if(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() {
}
}