文章目录
- 一. Filter 过滤器
- 1. 概括
- 2. 原理
- 3. api
- 配置过滤器(Filter)拦截路径
- 1.xml 方式
- 2.注解方式
- 4. 生命流程
- a.执行流程
- b.拦截路径
- c.过滤器链
- 5. 登录校验-Filter
一. Filter 过滤器
1. 概括
过滤器,顾名思义就是对事物进行过滤的,在 Web 中的过滤器,当然就是对请求进行过滤,我们使用过滤器,就可以对请求进行拦截,然后做相应的处理,实现许多特殊功能。如登录控制,权限管理,过滤敏感词汇等,是 JavaWeb 三大组件(Servlet、Filter、Listener)之一。
2. 原理
当我们使用过滤器时,过滤器会对浏览器的请求进行过滤,过滤器可以动态的分为 3 个部分,1.放行之前的代码,2.放行,3.放行后的代码,这 3 个部分分别会发挥不同作用。
- 第一部分代码会对浏览器请求进行第一次过滤,然后继续执行
- 第二部分代码就是将游览器请求放行,如果还有过滤器,那么就继续交给下一个过滤器
- 第三部分代码就是对返回的 Web 资源再次进行过滤处理
我们使用过滤器,也就是说,不止请求会经过过滤器,我们的响应也会经过过滤器。
3. api
定义过滤器
//定义一个类,实现一个标准的Filter过滤器的接口
public class DemoFilter implements Filter {
@Override //初始化方法, 只调用一次
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("init 初始化方法执行了");
}
@Override //拦截到请求之后调用, 调用多次
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
System.out.println("Demo 拦截到了请求...放行前逻辑");
//放行
chain.doFilter(request,response);
}
@Override //销毁方法, 只调用一次
public void destroy() {
System.out.println("destroy 销毁方法执行了");
}
}
- init 方法:过滤器的初始化方法。在 web 服务器启动的时候会自动的创建 Filter 过滤器对象,在创建过滤器对象的时候会自动调用 init 初始化方法,这个方法只会被调用一次。
- doFilter 方法:这个方法是在每一次拦截到请求之后都会被调用,所以这个方法是会被调用多次的,每拦截到一次请求就会调用一次 doFilter()方法。
- destroy 方法: 是销毁的方法。当我们关闭服务器的时候,它会自动的调用销毁方法 destroy,而这个销毁方法也只会被调用一次。
在过滤器 Filter 中,如果不执行放行操作,将无法访问后面的资源。 放行操作:chain.doFilter(request, response)
;
在定义完 Filter 之后,Filter 其实并不会生效,还需要完成 Filter 的配置,Filter 的配置非常简单,只需要在Filter类
上添加一个注解:@WebFilter
,并指定属性urlPatterns
,通过这个属性指定过滤器要拦截哪些请求。
@WebFilter(urlPatterns = "/*") //配置过滤器要拦截的请求路径( /* 表示拦截浏览器的所有请求 )
当我们在 Filter 类上面加了@WebFilter 注解之后,接下来我们还需要在启动类上面加上一个注解@ServletComponentScan,通过这个@ServletComponentScan 注解来开启 SpringBoot 项目对于 Servlet 组件的支持。
过滤器并不会管资源是否存在,而只会对配置的拦截路径进行拦截。拦截不仅会对请求进行拦截,而且还会对响应进行拦截。
配置过滤器(Filter)拦截路径
1.xml 方式
xml 方式可以说是和 Servlet 使用 xml 配置方式一样了。
<filter>
<filter-name>myFilter</filter-name>
<!-- <筛选器名称>myFilter</筛选器名称> -->
<filter-class>com.clucky.filter.MyFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>myFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
这个就是 xml 配置方式,只不过把注解换成了 xml 标签来配置,里面属性都是一样的,这个和 Servlet 的配置方式基本一样,这里就不再赘述了。
2.注解方式
使用@WebFilter
,里面的多个参数用,
进行分隔。
参数 | 说明 |
---|---|
filterName | 该 filter 的名字 |
initParams | 初始化参数 |
displayName | filter 显示名称 |
servletNames | 指定对哪些 servlet 进行过滤 |
asyncSupported | 是否支持异步模式 |
urlPatterns | 指定拦截路径 |
value | 指定拦截路径 |
**注意:**urlPatterns 和 value 是一样的。urlPatterns 和 value 只能配置一个,不能两个都配置,两个都配置就会报错。
4. 生命流程
a.执行流程
Filter 的执行流程主要包括初始化
、请求处理
、响应处理
和销毁
这四个阶段。放行操作,放行就是调用 FilterChain 对象当中的 doFilter()方法,在调用 doFilter()这个方法之前所编写的代码属于放行之前的逻辑。在放行后访问完 web 资源之后还会回到过滤器当中,回到过滤器之后如有需求还可以执行放行之后的逻辑,放行之后的逻辑我们写在 doFilter()这行代码之后。
b.拦截路径
拦截路径 | urlPatterns 值 | 含义 |
---|---|---|
拦截具体路径 | /login | 只有访问/login 路径是,才会被拦截 |
目录拦截 | /emps/* | 访问/emps 下的所有资源,都会被拦截 |
拦截所有 | /* | 访问所有资源,都会被拦截 |
c.过滤器链
所谓过滤器链指的是在一个 web 应用程序当中,可以配置多个过滤器,多个过滤器就形成了一个过滤器链。以注解方式配置的 Filter 过滤器,它的执行优先级是按时过滤器类名的自动排序确定的,类名排名越靠前,优先级越高
它用于将请求传递给过滤器链中的下一个过滤器或目标资源(如 Servlet)。在 Filter 的实现类中,通常会重写doFilter
方法来处理请求和响应。当一个过滤器完成其处理后
,它会调用doFilter
方法,并将控制权传递给下一个过滤器。如果没有其他过滤器需要处理该请求,那么控制权将传递给目标资源。
5. 登录校验-Filter
登录校验过滤器:LoginCheckFilter
@Slf4j
@WebFilter(urlPatterns = "/*") //拦截所有请求
public class LoginCheckFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
//前置:强制转换为http协议的请求对象、响应对象 (转换原因:要使用子类中特有方法)
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
//1.获取请求url
String url = request.getRequestURL().toString();
log.info("请求路径:{}", url); //请求路径:http://localhost:8080/login
//2.判断请求url中是否包含login,如果包含,说明是登录操作,放行
if(url.contains("/login")){
chain.doFilter(request, response);//放行请求
return;//结束当前方法的执行
}
//3.获取请求头中的令牌(token)
String token = request.getHeader("token");
log.info("从请求头中获取的令牌:{}",token);
//4.判断令牌是否存在,如果不存在,返回错误结果(未登录)
if(!StringUtils.hasLength(token)){
log.info("Token不存在");
Result responseResult = Result.error("NOT_LOGIN");
//把Result对象转换为JSON格式字符串 (fastjson是阿里巴巴提供的用于实现对象和json的转换工具类)
String json = JSONObject.toJSONString(responseResult);
response.setContentType("application/json;charset=utf-8");
//响应
response.getWriter().write(json);
return;
}
//5.解析token,如果解析失败,返回错误结果(未登录)
try {
JwtUtils.parseJWT(token);
}catch (Exception e){
log.info("令牌解析失败!");
Result responseResult = Result.error("NOT_LOGIN");
//把Result对象转换为JSON格式字符串 (fastjson是阿里巴巴提供的用于实现对象和json的转换工具类)
String json = JSONObject.toJSONString(responseResult);
response.setContentType("application/json;charset=utf-8");
//响应
response.getWriter().write(json);
return;
}
//6.放行
chain.doFilter(request, response);
}
}