Filter过滤器
一、引入
在和管理员有关的Controller中,接口都需要判断当前用户是否为管理员,如果是管理员,则可以操作目录;如果不是管理员,则不能操作;这一连串的身份验证代码都是固定的
,并且在每一个接口中都需要编写,显然代码没有得到重复利用
。为了解决这一问题,可以使用Servlet规范中的Filter过滤器
来解决这个问题。
二、Filter介绍
- Filter是过滤器(是一个Java程序)
- Filter可以在Servlet这个目标程序执行之前添加代码。也可以在目标Servlet执行之后添加代码。之前之后都可以添加过滤规则。
- 一般情况下,都是在过滤器当中编写公共代码。
三、Filter实现(原始实现方式)
-
第一步:编写一个Java类实现一个接口:
jarkata.servlet.Filter
。并且实现这个接口当中所有的方法。init方法
:在Filter对象第一次被创建之后调用,并且只调用一次。doFilter方法
:只要用户发送一次请求,则执行一次。发送N次请求,则执行N次。在这个方法中编写过滤规则。destroy方法
:在Filter对象被释放/销毁之前调用,并且只调用一次。
-
第二步:注册Filter
-
在web.xml文件中对Filter进行配置。这个配置和Servlet很像。
-
<filter> <filter-name>filter2</filter-name> <filter-class>com.bjpowernode.javaweb.servlet.Filter2</filter-class> </filter> <filter-mapping> <filter-name>filter2</filter-name> <url-pattern>*.do</url-pattern> </filter-mapping>
-
-
或者使用注解进行注册:@WebFilter({"*.do"})
-
// 访问这个/abc路径就会执行这个过滤器 @WebFilter("/abc") public class AdminFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { } @Override public void destroy() { } }
-
-
四、Filter相关知识点
-
注意:
- Servlet对象默认情况下,在服务器启动的时候是不会新建对象的。
- Filter对象默认情况下,在服务器启动的时候会新建对象。
- Servlet是单例的。Filter也是单例的。
(单实例)
-
目标Servlet是否执行,取决于两个条件:
- 第一:在过滤器当中是否编写了:chain.doFilter(request, response); 代码。
(放行)
- 第二:用户发送的请求路径是否和Servlet的请求路径一致。
- 第一:在过滤器当中是否编写了:chain.doFilter(request, response); 代码。
-
chain.doFilter(request, response); 这行代码的作用:
- 执行下一个过滤器,如果下面没有过滤器了,执行最终的Servlet
-
注意:Filter的优先级,天生的就比Servlet优先级高。
- /a.do 对应一个Filter,也对应一个Servlet。那么一定是先执行Filter,然后再执行Servlet。
-
关于Filter的配置路径:
- /a.do、/b.do、/dept/save。这些配置方式都是
精确匹配
。 - /*
匹配所有路径
。 - *.do
后缀匹配
。不要以 / 开始 - /dept/*
前缀匹配
- /a.do、/b.do、/dept/save。这些配置方式都是
-
在web.xml文件中进行配置的时候,Filter的执行顺序是什么?
- 依靠filter-mapping标签的配置位置,越靠上优先级越高。
-
过滤器的调用顺序,遵循
栈
数据结构。 -
使用@WebFilter的时候,Filter的执行顺序是怎样的呢?
- 执行顺序是:比较Filter这个类名。
- 比如:FilterA和FilterB,则先执行FilterA。
- 比如:Filter1和Filter2,则先执行Filter1。
-
Filter的生命周期?
- 和Servlet对象生命周期一致。
- 区别:Filter默认情况下,在服务器启动阶段就实例化。Servlet不会。
-
Filter过滤器这里有一个设计模式:
责任链设计模式
。- 过滤器最大的优点:
- 在程序编译阶段不会确定调用顺序。因为Filter的调用顺序是配置到web.xml文件中的,只要修改web.xml配置文件中filter-mapping的顺序就可以调整Filter的执行顺序。显然Filter的执行顺序是在程序运行阶段动态组合的。那么这种设计模式被称为责任链设计模式。
- 责任链设计模式最大的核心思想:
- 在程序运行阶段,动态的组合程序的调用顺序。
五、Spring Boot项目中使用Filter过滤器
过滤器是AOP(面向切面编程)思想的具体实现。可以过滤浏览器发出的请求,并且决定放行请求还是中断请求。
机制简述:
在浏览器对服务器发起请求或者服务器对浏览器响应,都会经过过滤器。
基于过滤器的机制,我们可以在过滤器中对请求和响应做一些处理,可以在过滤器中决定是否放行,例如:校验请求中有没有敏感字符串,校验有没有Session,实现URL级别的权限控制、压缩响应信息、编码格式等。
5.1 Spring Boot使用过滤器的两种方式
- 使用spring boot提供的
FilterRegistrationBean
注册Filter - 使用原生servlet注解@WebServlet,并在启动类上面增加注解@ServletComponentScan注册Filter
两种方法的本质都是一样的,都是去FilterRegistrationBean注册Filter
总结:定义Filter + 注册Filter
5.2 方法一:使用SpringBoot配置类实现
- 定义一个Filter类
- 定义一个Filter类实现Filter接口
- 重写三个方法:init、doFilter、destory
AdminFilter.java
package com.kyk.imoocmall.filter;
import com.kyk.imoocmall.common.Constant;
import com.kyk.imoocmall.pojo.User;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.io.PrintWriter;
/**
* 管理员校验过滤器
* @FileName imooc-mall
* @Author keyongkang
* @Create 2022-11-30-9:14
*/
public class AdminFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
request.setCharacterEncoding("utf-8");
servletResponse.setContentType("text/html;charset=utf-8");
servletResponse.setCharacterEncoding("utf-8");
HttpSession session = request.getSession();
// 对当前用户进行校验
User currentUser = (User)session.getAttribute(Constant.IMOOC_MALL_USER);
if (currentUser == null) {
// 用户未登录
//return R.error(ImoocMallExceptionEnum.USER_NOT_LOGIN);
PrintWriter out = new HttpServletResponseWrapper((HttpServletResponse) servletResponse).getWriter();
out.write("{\n" +
" \"status\": 10007,\n" +
" \"msg\": 用户未登录,\n" +
" \"data\": null\n" +
"}");
out.flush();
out.close();
return;
}
if (currentUser.getRole() != 2) {
// 非管理员操作
PrintWriter out = new HttpServletResponseWrapper((HttpServletResponse) servletResponse).getWriter();
out.write("{\n" +
" \"status\": 10009,\n" +
" \"msg\": \"当前用户不是管理员,无操作权限\",\n" +
" \"data\": null\n" +
"}");
out.flush();
out.close();
}
filterChain.doFilter(servletRequest, servletResponse);
}
@Override
public void destroy() {
}
}
该过滤器是用来校验管理员登录的。
- 注册Filter
AdminFilterConfig.java
package com.kyk.imoocmall.config;
import com.kyk.imoocmall.filter.AdminFilter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* Admin过滤器的配置
* @FileName imooc-mall
* @Author keyongkang
* @Create 2022-11-30-9:30
*/
@Configuration //定义此类为配置类,必须添加!
public class AdminFilterConfig {
@Bean
public AdminFilter adminFilter() {
return new AdminFilter();
}
// 使用spring boot提供的FilterRegistrationBean注册Filter
@Bean(name = "adminFilterConf")
public FilterRegistrationBean adminFilterConfig() {
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
filterRegistrationBean.setFilter(adminFilter());
filterRegistrationBean.addUrlPatterns("/admin/category/*");
filterRegistrationBean.addUrlPatterns("/admin/product/*");
filterRegistrationBean.addUrlPatterns("/admin/order/*");
filterRegistrationBean.setName("adminFilterConf");
return filterRegistrationBean;
}
}
总结:自定义一个Filter,然后自定义AdminFilterConfig类,使用spring boot提供的
FilterRegistrationBean
注册Filter
5.3 方法二:使用原生servlet注解@WebFilter + @ServletComponentScan
- 定义一个Filter类
在filter类上使用@WebFilter注解
package com.kyk.imoocmall.filter;
import com.kyk.imoocmall.common.Constant;
import com.kyk.imoocmall.pojo.User;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.io.PrintWriter;
/**
* 管理员校验过滤器
* @FileName imooc-mall
* @Author keyongkang
* @Create 2022-11-30-9:14
*/
@WebFilter(urlPatterns = "/admin/category/*") // 注意
public class AdminFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
request.setCharacterEncoding("utf-8");
servletResponse.setContentType("text/html;charset=utf-8");
servletResponse.setCharacterEncoding("utf-8");
HttpSession session = request.getSession();
// 对当前用户进行校验
User currentUser = (User)session.getAttribute(Constant.IMOOC_MALL_USER);
if (currentUser == null) {
// 用户未登录
//return R.error(ImoocMallExceptionEnum.USER_NOT_LOGIN);
PrintWriter out = new HttpServletResponseWrapper((HttpServletResponse) servletResponse).getWriter();
out.write("{\n" +
" \"status\": 10007,\n" +
" \"msg\": 用户未登录,\n" +
" \"data\": null\n" +
"}");
out.flush();
out.close();
return;
}
if (currentUser.getRole() != 2) {
// 非管理员操作
//return R.error(ImoocMallExceptionEnum.NOT_ADMIN);
PrintWriter out = new HttpServletResponseWrapper((HttpServletResponse) servletResponse).getWriter();
out.write("{\n" +
" \"status\": 10009,\n" +
" \"msg\": \"当前用户不是管理员,无操作权限\",\n" +
" \"data\": null\n" +
"}");
out.flush();
out.close();
}
filterChain.doFilter(servletRequest, servletResponse);
}
@Override
public void destroy() {
}
}
-
在启动类上面增加注解@ServletComponentScan开启Servlet支持
package com.kyk.imoocmall;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;
import springfox.documentation.oas.annotations.EnableOpenApi;
@EnableOpenApi
@SpringBootApplication
@MapperScan("com.kyk.imoocmall.mapper")
@ServletComponentScan("com.kyk.imoocmall.filter") //扫描指定指定的filter包
public class ImoocMallApplication {
public static void main(String[] args) {
SpringApplication.run(ImoocMallApplication.class, args);
}
}
标签:Spring,Boot,Filter,过滤器,import,servlet,public,out From: https://www.cnblogs.com/keyongkang/p/16939006.html注意:这里直接用@WebFilter就可以进行配置,同样,可以设置url匹配模式,过滤器名称等。这里需要注意一点的是@WebFilter这个注解是Servlet3.0的规范,并不是Spring boot提供的。除了这个注解以外,我们需在启动类中加注解:@ServletComponetScan,指定扫描的包。