在SpringBoot
开发web
应用的过程中,常常会使用到拦截器。
而拦截器是一个很常用的功能,它支持我们在HTTP
请求到达Controller
之前添加一些自定义的逻辑
比如说,在到达Controller
之前,将对一些敏感词汇进行检测,一旦出现敏感词,看你是过滤,还是直接返回异常
这就是拦截器的一个简单应用,通过本篇文章,您将会了解到SpringBoot
拦截器的功能使用
拦截器的使用可以分为两个步骤
HandlerInterceptor
,编写自己的拦截器首先我们先确定好自己需要做什么,就按照前言那样说的,我们对request
中的body
参数进行检测,只要有敏感词就将异常抛出
但是有个问题,HttpServletRequest
的输入流只够读取一次,如果拦截器这边用了,那么后面controller
就读取不到body
流了
解决办法也是有的,就是使用HttpServletRequestWrapper
装饰,在进入的时候HttpServletRequest
替换为这个装饰类
我们先写一个类,用来继承实现自己的缓存
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
,使用过滤器即可
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
流的情况就解决了
现在我们再来编写我们的拦截器
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);
}
}
整个判断非常简单,如果请求参数中有敏感词,则拦截请求,返回错误信息
拦截器我们写完了,但按照前面所说的步骤,我们还需要将这个拦截器进行注册,代码如下
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
方式进行传参
先来看看成功的请求,能够正常进行插入
再来看看请求失败,被拦截的请求
为什么HttpServletRequest
的输入流只能读取一次呢?当我们调用其getInputStream()
方法,返回来的只是一个输入流对象,实际类型为ServletInputStream
这个对象read()
方法内部维护了一个属性,标记着当前读取字节的坐标位置
想要重新开始读取,只能将这个属性重新设置为0
而InputStream
当中,有一个reset()
方法,但是方法中会抛出异常,只能交给子类去实现
但巧的是ServletInputStream
没有重写这个方法,所以重复读取就会显得很麻烦
参考文档:SpringBoot项目中,拦截器获取Post方法的请求body_拦截器获取body参数-CSDN博客
好在,我们可以用另外一种方式去解决,这样一来,我们就能实现拦截器所需要的功能了
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
扫码关注腾讯云开发者
领取腾讯云代金券
Copyright © 2013 - 2025 Tencent Cloud. All Rights Reserved. 腾讯云 版权所有
深圳市腾讯计算机系统有限公司 ICP备案/许可证号:粤B2-20090059 深公网安备号 44030502008569
腾讯云计算(北京)有限责任公司 京ICP证150476号 | 京ICP备11018762号 | 京公网安备号11010802020287
Copyright © 2013 - 2025 Tencent Cloud.
All Rights Reserved. 腾讯云 版权所有