若想在每次请求时均在后台处理以下逻辑: ![](https://img.kancloud.cn/70/fe/70fe2ea55b9d93f1a3a01ff719a3fec0_296x194.png) 按当前掌握的知识便需要在每个控制器的方法中加入相应的处理认证令牌的代码。以上个章节中刚刚完成的StudentController为例,示例代码如下: ```java public class StudentController { ... @GetMapping("{id}") public Student getById(@PathVariable Long id) { // 进行令牌认证与分发 ➊ return this.studentService.findById(id); } @PostMapping @ResponseStatus(HttpStatus.CREATED) public Student save(@RequestBody Student student) { // 进行令牌认证与分发 ➋ return studentService.save(student); } ``` * ➊➋ 在进行逻辑处理前先进行令牌的验证与分发。 这种方案是可行的,按此方案则需要先开发一个验证与分发令牌的方法,然后在各个控制器的方法中来调用此方法来完成令牌分发的操作。 同时这种方案由于对原有代码进行了过多的干预,所以必然也不是最佳的。在团队开发的过程中,我们希望能够将认证的模块与其它的模块可以做到单独、并列开发。每个模块都是互相独立的。尽量的降低各个模块间的耦合度。 # filter过滤器 成熟的spring当前早早的就已经解决了此类问题并形成了最佳实践,那就是filter过滤器。在计算机的世界里,过滤器与现实生活中稍有不同。现实生活中的过滤器的最终目的都是把不符合要求的过滤掉,把符合要求的留下来,所以过滤后的物质不会多于过滤前的物质。而计算机中的过滤器即可以越过滤越少,也可以越过滤越多。过滤后的数据的多少完全取决于所实现的代码。 不止如此spring中的过滤器在进行http请求过滤时,不但可以过滤请求的数据,还可以过滤返回的数据。加入过滤器器具前后台的令牌交互的过程如下图: ![](https://img.kancloud.cn/2a/cd/2acdc94ee4440132f4a062dbf7232b9b_809x471.png) ## 实现过滤器 使用idea打开后台项目,并在项目根目录下新建`filter`包,然后在此包下新建`TokenFilter`类。 ![](https://img.kancloud.cn/a4/60/a4603eb777d1481b5411047c21952271_506x321.png) 初始化如下: ``` package com.mengyunzhi.springbootstudy.filter; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.http.HttpFilter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; /** * 令牌过滤器 * 继承HttpFilter以过滤http请求与响应 * @author panjie */ public class TokenFilter extends HttpFilter ➊{ @Override ➋ protected void doFilter(HttpServletRequest request➌, HttpServletResponse response➍, FilterChain chain) throws IOException, ServletException { // 获取 header中的token并做有效性验证 // 如果无效则分发送的token // 转发数据。spring开始调用控制器中的特定方法 chain.doFilter(request, response); // 为http响应加入新token后返回 } } ``` * ➊ 继承HttpFilter。该HttpFilter进行一些自动判断后将数据发送到doFilter方法。 * ➋ 此doFilter方法已存在于HttpFilter中,所以在此以关键字声明 * ➌ 改变此对象可以达到改变http请求的目的 * ➍ 改变此对象可以达到改变http响应的目的 ## 配置过滤器 若想使某个过滤器生效,还需要:一、对项目进行配置以启用其打描过滤器的功能;二、使用特定的注解来告知spring某个过滤器此时的状态为**生效**。 有了这两项配置以后spring在启动应用时便会将声明了**生效**状态的过滤器加入到项目中来了。此时若有http请求则会执行过滤器中的代码。在进行配置以前,分别于TokenFilter及TeacherController中加入以下日志代码以更好的在控制台中查看其执行过程。 filter/TokenFilter.java ```java public class TokenFilter extends HttpFilter { private final static Logger logger = LoggerFactory.getLogger(TokenFilter.class); ✚ @Override protected void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException { // 获取 header中的token并做有效性验证 // 如果无效则分发送的token logger.info("在控制器被调用以前执行"); ✚ // 转发数据。spring开始调用控制器中的特定方法 chain.doFilter(request, response); logger.info("在控制器被调用以后执行"); ✚ // 为http响应加入新token后返回 } } ``` controller/TeacherController.java ```java @GetMapping @CrossOrigin("*") public List<Teacher> getAll() { logger.info("调用TeacherController的getAll方法"); ... ``` 然后于IDEA中建立一个Http Request(忘记如何建立的话请参数2.4.2)测试,内容如下: ``` GET http://localhost:8080/Teacher ``` 启动数据库及后台应用后,执行该测试并观察控制台: ![](https://img.kancloud.cn/70/03/70031102f47f7c183d6a6b531379b40b_654x136.png) ``` 2020-02-10 16:39:50.733 INFO 24047 --- [nio-8080-exec-4] c.m.s.controller.TeacherController : 调用TeacherController的getAll方法 ``` 在执行http requrest时,控制台中新增了一条info等级的日志。说时此时TokenFilter中的doFilter方法并未执行。 ### 启用过滤器扫描功能 SpringBootStudyApplication.java ```java @SpringBootApplication @ServletComponentScan ➊ public class SpringBootStudyApplication { ``` * ➊ 启用Servlet组件扫描功能。 >[info] ServletComponent有三种类型:WebServlet、WebFilter及WebListener ### 加入注解 filter/TokenFilter.java ``` @WebFilter ➊ public class TokenFilter extends HttpFilter { ``` * ➊ 告知spring: 本类是个生效的过滤器 ## 测试 重新启动后台应用并发起http request测试,查看控制台得到如下info等级信息。 去除debug等级的日志后如下 ``` 2020-02-10 16:45:40.563 INFO 29832 --- [nio-8080-exec-1] c.m.springbootstudy.filter.TokenFilter : 在控制器被调用以前执行 ... 2020-02-10 16:45:40.578 INFO 29832 --- [nio-8080-exec-1] c.m.s.controller.TeacherController : 调用TeacherController的getAll方法 ... 2020-02-10 16:45:40.623 INFO 29832 --- [nio-8080-exec-1] c.m.springbootstudy.filter.TokenFilter : 在控制器被调用以后执行 ``` # 参考文档 | 名称 | 链接 | 预计学习时长(分) | | --- | --- | --- | | 源码地址 | [https://github.com/mengyunzhi/spring-boot-and-angular-guild/releases/tag/step5.2.1](https://github.com/mengyunzhi/spring-boot-and-angular-guild/releases/tag/step5.2.1) | - | | servlet filter | [https://www.oracle.com/technetwork/java/filters-137243.html](https://www.oracle.com/technetwork/java/filters-137243.html) | - | | @ServletComponentScan | [https://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/web/servlet/ServletComponentScan.html](https://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/web/servlet/ServletComponentScan.html) | - | | @WebFilter | [https://docs.oracle.com/javaee/7/api/javax/servlet/annotation/WebFilter.html?is-external=true](https://docs.oracle.com/javaee/7/api/javax/servlet/annotation/WebFilter.html?is-external=true) | - |