自定义拦截器实现

在 Spring MVC 框架中, 拦截器作为一种机制, 用于对请求进行拦截. 拦截器可以在请求进入处理器之前、处理器返回处理之后、视图渲染之前等各个环节进行拦截.
拦截器通常用于实现一下功能 :

  • 鉴权和身份认证
  • 日志记录和统计
  • 请求参数和校验和过滤
  • 缓存和性能优化
  • 路径重定向和错误处理

也就是说, 当需要对多个请求进行相同或者类似处理时, 可以使用拦截器来统一处理, 从而避免冗余的代码和低效的维护.

一. 自定义拦截器实现

1. 创建一个 Spring Boot 项目, 添加 Spring Web、 Lombok 起步依赖.

正常创建一个 Spring Boot 的 Maven 项目即可.

2. 创建一个公共包 common 实现登录拦截器类

在 Spring MVC 中, 拦截器是通过实现 HandlerInterceptor 接口实现的.
在实现 HandlerInterceptor 接口时, 重写里面的拦截器方法.
自定义拦截器实现_第1张图片

a. preHandle

该方法用于在请求处理之前进行拦截和处理. 该方法在 DispatcherServlet ( 调度器 ) 将请求对象封装为 HandlerExecutionChain 对象后、但在调用处理器( Controller ) 之前被调用
在 preHandle 方法中, 我们可以对请求进行检查、验证、修改等操作, 例如 :

  • 身份认证和权限控制
  • 请求参数校验和过滤
  • 缓存和性能优化

b. postHandle

该方法用于在请求处理完成之后, 试图渲染之前进行拦截和处理. 该方法在 HandlerAdapter 将处理器执行并将其结果返回给 DispatcherServlet 之后调用.
在 postHandle 方法中, 我们可以做一些与请求处理有关的操作, 例如 :

  • 修改 Model 数据
  • 添加一些公共的 Model 属性
  • 对视图进行渲染前的处理

c. afterCompletion

该方法用于请求处理完毕后进行回收和清理工作, 该方法在视图渲染完毕之后被调用, 用于释放资源和进行日志记录等操作
在 afterCompletion 方法中, 我们可以进行一些与请求处理过程相关的清理工作, 例如 :

  • 清理 ThreadLocal 变量
  • 关闭打开的文件或数据库连接
  • 记录请求处理时间和结果

3. 重写方法

由于我们是一个登陆拦截器, 可以想象在各个功能使用之前就需要进行权限的鉴定和登陆的检验. 因此应该重写 preHandle 方法, 在请求处理之前进行拦截和处理

@Component
    public class LoginInterceptor implements HandlerInterceptor {

        /**
* 拦截器为调用方法执行之前执行的方法
*
* @param request  请求
* @param response 响应
* @param handler  表示要执行的处理器, 即处理请求的 Controller 对象或者是拦截器的对象
* @return true 拦截器验证成功, 继续执行后续操作
* false 拦截器验证失败, 验证未通过后续流程不执行
* @throws Exception 异常
*/
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

            // 用户登录判断
            HttpSession session = request.getSession(false);
            if (session != null && session.getAttribute("session_userinfo") != null) {
                // 该用户已经登陆
                return true;
            }
            // 请求无权限, 返回状态码提示
            response.setStatus(401);
            return false;
        }
    }

4. 添加拦截器配置

有了拦截器, 但还没有实现拦截规则, 并且这个拦截的规则是要随着 Web 项目的启动而启动的, 否则在进行页面访问时, 就无法提前拦截了. 因此还需要将自定义的拦截器添加到我们的系统配置当中.
而在 Spring MVC 中提供了一个 WebMvcConfigurer 接口, 因为我们配置的是拦截器, 因此重写里面的 addInterceptors 方法即可.
自定义拦截器实现_第2张图片
在重写的方法中, 将我们实现的登陆拦截器添加进去, 然后配置拦截的规则即可

@Configuration
    public class MyInterceptorConfig implements WebMvcConfigurer {

        // 将自定义的拦截器注入
        @Autowired
        private LoginInterceptor interceptor;

        /**
* @param registry 注册对象
*/
        @Override
        public void addInterceptors(InterceptorRegistry registry) {

            // 注册对象将拦截器添加进框架中
            registry.addInterceptor(interceptor)
                // 配置拦截规则
                .addPathPatterns("/**") // 拦截所有的 url
                .excludePathPatterns("/user/reg") // 排除用户注册 url
                .excludePathPatterns("/user/login") // 排除用户登陆 url
                .excludePathPatterns("/image/**"); // 排除 image 文件夹底下的所有文件
        }
    }

一般对于拦截规则的设置, 我们都是先拦截所有, 然后放行部分. 并且里面的路径写法和我们之前学习的相同.

5. 验证拦截器

当写好拦截器后, 我们创建一个用户操作类

@RestController
    @RequestMapping("/user")
    public class UserController {

        @RequestMapping("/reg")
        public String register() {
            return "注册成功";
        }

        @RequestMapping("/login")
        public String login() {
            return "登陆成功";
        }

        @RequestMapping("article")
        public String writer() {
            return "写文章鉴权";
        }
    }

根据我们的拦截规则, 我们排除了 url 为 /user/reg 和 user/login 以及 image 文件底下的所有文件. 因此这三个都是可以访问的, 其他的 ulr 都是不可以访问的, 也就是我们上面写的 /user/article 是无法访问的, 会被拦截并返回 401

可以看到, 我们放行的方法都能够正确经过拦截器的处理
自定义拦截器实现_第3张图片自定义拦截器实现_第4张图片

而我们的 /user/article 被拦截器拦截在外无法执行
自定义拦截器实现_第5张图片

6. 调整返回错误信息

对于我们刚刚的做法中, 当被拦截以后, 说明这个方法是不符合要求的请求, 直接返回了 401 拒绝访问, 但在开发中, 无论是否被拦截, 都应该将信息返回给前端, 而并非返回页面而是要一个数据, 前端根据我们返回的数据进行判断后再执行下一步操作.
因此, 我们可以封装一个统一的返回对象 ResultAjax
即使后端拦截了这个方法, 它没有进行登录, 我们也不能帮前端进行重定向让给用户去登录, 这是要越权行为. 我们只需要返回约定的错误代码 code 告诉前端, 并把错误信息返回给前端即可.

@Data
    public class ResultAjax {
        private int code;
        private String msg;
        private String date;
    }

当被拦截时, 说明为经过登陆鉴权, 返回约定的对象即可

@Component
    public class LoginInterceptor implements HandlerInterceptor {

        /**
* 拦截器为调用方法执行之前执行的方法
*
* @param request  请求
* @param response 响应
* @param handler  表示要执行的处理器对象, 即处理请求的 Controller 对象或者是拦截器的对象
* @return true 拦截器验证成功, 继续执行后续操作
* false 拦截器验证失败, 验证未通过后续流程不执行
* @throws Exception 异常
*/
        @Autowired
        private ResultAjax resultAjax;

        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

            // 用户登录判断
            HttpSession session = request.getSession(false);
            if (session != null && session.getAttribute("session_userinfo") != null) {
                // 该用户已经登陆
                return true;
            }

            ObjectMapper objectMapper = new ObjectMapper();

            // 设置数据返回格式, 防止乱码解析
            response.setContentType("application/json; charset = utf8");

            // 当前方法未通过登陆鉴权, 返回约定的结果
            resultAjax.setCode(-1); // 约定错误都为 -1 状态码, 成功都为 200
            resultAjax.setMsg("用户未登录, 请登录后再操作 !");
            resultAjax.setDate(""); // 为登陆没有数据返回

            // 将返回的数据对象转为 JSON 格式字符串
            String respJson = objectMapper.writeValueAsString(resultAjax);

            // 返回统一的处理对象给前端
            response.getWriter().write(respJson);
            return false;
        }
    }

自定义拦截器实现_第6张图片
这是一般我们的处理情况, 最好的还是进行一个统一的格式的错误信息返回. 关于统一的数据处理和错误处理, 在后续文章中会讲解到.

你可能感兴趣的:(java,spring,mvc,spring,boot,后端)