前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >SpringBoot拦截器的使用

SpringBoot拦截器的使用

原创
作者头像
半月无霜
发布于 2025-01-10 07:41:05
发布于 2025-01-10 07:41:05
21600
代码可运行
举报
文章被收录于专栏:半月无霜半月无霜
运行总次数:0
代码可运行

一、前言

SpringBoot开发web应用的过程中,常常会使用到拦截器。

而拦截器是一个很常用的功能,它支持我们在HTTP请求到达Controller之前添加一些自定义的逻辑

比如说,在到达Controller之前,将对一些敏感词汇进行检测,一旦出现敏感词,看你是过滤,还是直接返回异常

这就是拦截器的一个简单应用,通过本篇文章,您将会了解到SpringBoot拦截器的功能使用

二、代码

拦截器的使用可以分为两个步骤

  1. 实现HandlerInterceptor,编写自己的拦截器
  2. 对上面写好的拦截器进行注册

首先我们先确定好自己需要做什么,就按照前言那样说的,我们对request中的body参数进行检测,只要有敏感词就将异常抛出

但是有个问题,HttpServletRequest的输入流只够读取一次,如果拦截器这边用了,那么后面controller就读取不到body流了

解决办法也是有的,就是使用HttpServletRequestWrapper装饰,在进入的时候HttpServletRequest替换为这个装饰类

我们先写一个类,用来继承实现自己的缓存

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 package com.banmoon.config;import javax.servlet.ReadListener;
 import javax.servlet.ServletInputStream;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletRequestWrapper;
 import java.io.*;public class CachedBodyHttpServletRequest extends HttpServletRequestWrapper {private final byte[] cachedBody;public CachedBodyHttpServletRequest(HttpServletRequest request) throws IOException {
         super(request);
         InputStream requestInputStream = request.getInputStream();
         this.cachedBody = toByteArray(requestInputStream);
     }
 ​
     @Override
     public ServletInputStream getInputStream() throws IOException {
         return new CachedBodyServletInputStream(this.cachedBody);
     }
 ​
     @Override
     public BufferedReader getReader() throws IOException {
         ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(this.cachedBody);
         return new BufferedReader(new InputStreamReader(byteArrayInputStream));
     }private byte[] toByteArray(InputStream input) throws IOException {
         ByteArrayOutputStream output = new ByteArrayOutputStream();
         byte[] buffer = new byte[1024];
         int n;
         while ((n = input.read(buffer)) != -1) {
             output.write(buffer, 0, n);
         }
         return output.toByteArray();
     }private static class CachedBodyServletInputStream extends ServletInputStream {
         private final ByteArrayInputStream buffer;public CachedBodyServletInputStream(byte[] contents) {
             this.buffer = new ByteArrayInputStream(contents);
         }
 ​
         @Override
         public boolean isFinished() {
             return buffer.available() == 0;
         }
 ​
         @Override
         public boolean isReady() {
             return true;
         }
 ​
         @Override
         public void setReadListener(ReadListener readListener) {
             throw new UnsupportedOperationException();
         }
 ​
         @Override
         public int read() throws IOException {
             return buffer.read();
         }
     }
 }

有了此类之后,我们还需要在拦截器之前,将我们的HttpServletRequest替换为CachedBodyHttpServletRequest,使用过滤器即可

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 package com.banmoon.config;import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Component;import javax.servlet.*;
 import javax.servlet.http.HttpServletRequest;
 import java.io.IOException;
 ​
 @Slf4j
 @Component
 public class ReplaceStreamFilter implements Filter {
 ​
     @Override
     public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
         ServletRequest requestWrapper = new CachedBodyHttpServletRequest((HttpServletRequest) request);
         chain.doFilter(requestWrapper, response);
     }
 }

如此一来,body流的情况就解决了


现在我们再来编写我们的拦截器

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 package com.banmoon.config;import cn.hutool.core.io.IoUtil;
 import com.banmoon.business.exception.BanmoonException;
 import org.jetbrains.annotations.NotNull;
 import org.springframework.stereotype.Component;
 import org.springframework.web.servlet.HandlerInterceptor;import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import java.io.BufferedReader;
 ​
 @Component
 public class SensitiveWordsInterceptor implements HandlerInterceptor {public static final String SENSITIVE_WORDS = "敏感词";
 ​
     @Override
     public boolean preHandle(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull Object handler) throws Exception {
         BufferedReader reader = request.getReader();
         String read = IoUtil.read(reader);
         if (read.contains(SENSITIVE_WORDS)) {
             throw new BanmoonException("包含敏感词");
         }
         return HandlerInterceptor.super.preHandle(request, response, handler);
     }
 }

整个判断非常简单,如果请求参数中有敏感词,则拦截请求,返回错误信息

拦截器我们写完了,但按照前面所说的步骤,我们还需要将这个拦截器进行注册,代码如下

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 package com.banmoon.config;import org.springframework.context.annotation.Configuration;
 import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
 import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;import javax.annotation.Resource;
 ​
 @Configuration
 public class WebMvcConfig extends WebMvcConfigurationSupport {
 ​
     @Resource
     private SensitiveWordsInterceptor sensitiveWordsInterceptor;
 ​
     @Override
     protected void addInterceptors(InterceptorRegistry registry) {
         registry.addInterceptor(sensitiveWordsInterceptor)
                 .addPathPatterns("/**");
     }
 }

继承WebMvcConfigurationSupport类,重写addInterceptors方法,将拦截器添加进去即可

同时,也可以指定拦截器所拦截的路径,/**代表所有路径


那么现在可以启动项目,来进行验证了,controller接口我找了一个以前的一个插入更新的接口,使用body方式进行传参

先来看看成功的请求,能够正常进行插入

image-20250110152032022
image-20250110152032022

再来看看请求失败,被拦截的请求

image-20250110152301337
image-20250110152301337

三、最后

为什么HttpServletRequest的输入流只能读取一次呢?当我们调用其getInputStream()方法,返回来的只是一个输入流对象,实际类型为ServletInputStream

这个对象read()方法内部维护了一个属性,标记着当前读取字节的坐标位置

想要重新开始读取,只能将这个属性重新设置为0

InputStream当中,有一个reset()方法,但是方法中会抛出异常,只能交给子类去实现

但巧的是ServletInputStream没有重写这个方法,所以重复读取就会显得很麻烦

参考文档:SpringBoot项目中,拦截器获取Post方法的请求body_拦截器获取body参数-CSDN博客

好在,我们可以用另外一种方式去解决,这样一来,我们就能实现拦截器所需要的功能了

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验