过滤器Filter
-
Filter
- Filter表示过滤器,是 JavaWeb三大组件(Servlet、Filter、Listener)之一
- 作用: 过滤器可以把对资源的请求拦截下来,从而实现一些特殊的功能
- 使用了过滤器之后,要想访问web服务器上的资源,必须先经过滤器,过滤器处理完毕之后, 才可以访问对应的资源。
- 通用操作: 登录校验、统一编码处理、敏感字符处理等
Filter的基本使用操作
-
定义过滤器
-
定义一个类,实现 Filter 接口,并重写其所有方法
-
//配置过滤器要拦截的请求路径( /* 表示拦截浏览器的所有请求 ) //@WebFilter(urlPatterns = "/*") //定义一个类,实现一个标准的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,而这个销毁方法也只会被调用一次。
- init方法:
-
-
配置过滤器
在定义完Filter之后,Filter其实并不会生效,还需要完成Filter的配置
-
Filter的配置
- 1.Filter类上加 @WebFilter 注解,并通过属性urlPatterns配置拦截资源的路径。
- 2.引导类上加 @ServletComponentScan 开启Servlet组件支持
-
Filter类上加 @WebFilter 注解
//配置过滤器要拦截的请求路径( /* 表示拦截浏览器的所有请求 ) @WebFilter(urlPatterns = "/*") //定义一个类,实现一个标准的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 销毁方法执行了"); } }
-
在引导类上面加上一个注解 @ServletComponentScan
@ServletComponentScan @SpringBootApplication public class TliasWebManagementApplication { public static void main(String[] args) { SpringApplication.run(TliasWebManagementApplication.class, args); } }
-
-
注意事项:
在过滤器Filter中,如果不执行放行操作,将无法访问后面的资源。
放行操作: chain.doFilter(request, response);
Filter详解
过滤器的执行流程
1.过滤器拦截到了请求之后,执行放行前的逻辑【调用doFilter()方法前的代码属于放行之前的逻辑】
2.然后调用 FilterChain对象当中的doFilter()方法放行。
3.放行后访问 web 资源
4.之后回到过滤器当中,执行放行之后的逻辑【放行之后的逻辑写在doFilter()这行代码之后】
@Override //拦截到请求之后调用, 调用多次
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
System.out.println("Demo 拦截到了请求...放行前逻辑");
//放行
chain.doFilter(request,response);
System.out.println("Demo 拦截到了请求...放行后逻辑");
}
过滤器的拦截路径配置
拦截路径 | urlPatterns值 | 含义 |
---|---|---|
拦截具体路径 | /login | 只有访问 /login 路径时,才会被拦截 |
目录拦截 | /emps/* | 访问/emps下的所有资源,都会被拦截 |
拦截所有 | /* | 访问所有资源,都会被拦截 |
过滤器链
过滤器链指的是在一个web应用程序当中,可 以配置多个过滤器,多个过滤器就形成了一个过滤器链。
-
举例说明
在web服务器当中,定义了两个过滤器,这两个过滤器就形成了一个过滤器链。(如上图所示)
链上的过滤器在执行的时候会一个一个的执行,会先执行第一个Filter,放行之后再来执行第二 个Filter,如果执行到了最后一个过滤器放行之后,才会访问对应的web资源
访问完web资源之后,还会回到过滤器当中来执行过滤器放行后的逻辑,而在执行放行后的逻辑的时候,顺序是反着的。先要执行过滤器2放行之后的逻辑,再来执行过滤器1放行之后的逻辑,最后在给浏览器响应数据。
-
过滤器链中过滤器优先级
- 以注解方式配置的Filter过滤器,它的执行优先级是按过滤器类名的自动排序确定的,类名排名越靠前,优先级越高。
- 想要修改过滤器链中过滤器的执行顺序,可以修改类名
登录校验-Filter
-
登录校验的基本流程:
-
访问登录接口login。
-
登录成功之后,在服务端生成一个JWT令牌,并且把JWT令牌返回给前端,前端会将JWT令牌存储下来。
-
在后续的每次请求当中,都会将JWT令牌携带到服务端,请求到达服务端之后,先要校验令牌的有效性。
- 除了登录请求外,所有的请求,拦截到了之后,都需要校验令牌
-
对于校验令牌使用登录校验的过滤器,在过滤器当中来校验令牌的有效性。
-
如果令牌是无效的,就响应一个错误的信息,也不会再去放行访问对应的资源了。
-
如果令牌存在且有效,此时就会放行去访问对应的web资源,执行相应的业务操作。
-
-
具体流程【利用Filter过滤器】
-
代码实现
-
第三方json处理的工具包fastjson
-
fastjson是阿里巴巴提供的,用于实现对象和json的转换工具类
//将对象转换成json格式的字符串 String json=JSONObject.toJSONString(对象);
-
使用时 需要引入如下依赖
<dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.76</version> </dependency>
-
-
登录校验过滤器: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格式字符串 String json = JSONObject.toJSONString(responseResult); response.setContentType("application/json;charset=utf8"); //响应 response.getWriter().write(json); return; } //5.解析token,如果解析失败,返回错误结果(未登录) try { JwtUtils.parseJWT(token); }catch (Exception e){ log.info("令牌解析失败!"); Result responseResult = Result.error("NOT_LOGIN"); //把Result对象转换为JSON格式字符串 String json = JSONObject.toJSONString(responseResult); response.setContentType("application/json;charset=utf8"); //响应 response.getWriter().write(json); return; } //6.放行 chain.doFilter(request, response); } }
-