在SpringBoot中拦截修改请求Body的2种正确方式
在Web应用中,客户端发送的请求数据可能不符合后端服务的直接处理要求,如格式不匹配、不文明用语、数据不完整或包含敏感信息。通过修改请求Body,可以在数据到达Controller之前进行必要的格式化、验证、脱敏等处理,确保数据的准确性和安全性。拦截器可以拦截传入的 HTTP 请求,并在控制器处理这些请求之前对其进行处理。理解了Filter的工作原理后,接下来定义一个Filter,该Filter作用
原创 Springboot实战案例锦集 Spring全家桶实战案例源码 2024年08月24日 11:30 新疆 标题已修改
Spring全家桶实战案例源码
spring, springboot, springcloud 案例开发详解
559篇原创内容
公众号
环境:SpringBoot3.2.5
1. 简介
修改请求Body内容的需求源于多种场景,其中最重要的是数据预处理和安全性考虑。在Web应用中,客户端发送的请求数据可能不符合后端服务的直接处理要求,如格式不匹配、不文明用语、数据不完整或包含敏感信息。通过修改请求Body,可以在数据到达Controller之前进行必要的格式化、验证、脱敏等处理,确保数据的准确性和安全性。同时这促进了松散耦合,大大减少了开发工作量。
接下来我将通过2种方式来实现如何在请求到底Controller之前进行请求body的修改。
2. 实战案例
2.1 准备接口
需要先准备一个后端接口,用来接收请求body内容。
public class Article {
private String title ;
private String content ;
// getters, setters
}
// 接口
@RestController
@RequestMapping("/modifybody")
public class ModifyBodyController {
@PostMapping
public Article save(@RequestBody Article article) {
return article ;
}
}
接口定义非常简单,并没有任何的其它处理。接下来将基于该接口实现在不修改Controller接口的前提下,修改请求中title或者是content中的特殊字符。
2.2 基于Filter实现
过滤器是解决在任何 Servlet 容器中运行的应用程序的这些通用问题的最佳选择。下图是过滤器的工作原理图:
理解了Filter的工作原理后,接下来定义一个Filter,该Filter作用是用来转义 HTTP 请求正文中的所有 HTML 字符,以防止 XSS 攻击。
过滤器定义
@Component
@Order(1)
public static class EscapeHtmlFilter implements Filter {
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
throws IOException, ServletException {
filterChain.doFilter(new EscapeHtmlRequestWrapper((HttpServletRequest) servletRequest), servletResponse);
}
}
注意,这里我们重新包装了HttpServletRequest对象,否则将会出现Request Body只能读取一次的情况。
public static class EscapeHtmlRequestWrapper extends HttpServletRequestWrapper {
private String body = null;
public EscapeHtmlRequestWrapper(HttpServletRequest request) throws IOException {
super(request);
this.body = HtmlUtils.escapeHtml(request);
}
public ServletInputStream getInputStream() throws IOException {
final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body.getBytes());
ServletInputStream servletInputStream = new ServletInputStream() {
@Override
public int read() throws IOException {
return byteArrayInputStream.read();
}
// 这里还有其它的方法,默认即可
};
return servletInputStream;
}
public BufferedReader getReader() {
try {
return new BufferedReader(new InputStreamReader(this.getInputStream()));
} catch (IOException e) {}
return null;
}
}
以上就完成了关键的代码,接下来就可以进行测试了
正确的将<script>标签删除了。
2.3 通过AOP实现
通过RequestBodyAdvice 接口和注解 @RestControllerAdvice可用于所有 REST 控制器。利用它们在 HTTP 请求到达控制器之前转义 HTML 字符:
@RestControllerAdvice
public class EscapeHtmlBodyAdvice implements RequestBodyAdvice {
public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType,
Class<? extends HttpMessageConverter<?>> converterType) throws IOException {
InputStream is = inputMessage.getBody() ;
return new HttpInputMessage() {
public InputStream getBody() throws IOException {
return new ByteArrayInputStream(HtmlUtils.escapeHtml(is).getBytes(StandardCharsets.UTF_8)) ;
}
public HttpHeaders getHeaders() {
return inputMessage.getHeaders() ;
}
} ;
}
public boolean supports(MethodParameter methodParameter, Type targetType,
Class<? extends HttpMessageConverter<?>> converterType) {
return true ;
}
public Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType,
Class<? extends HttpMessageConverter<?>> converterType) {
return body ;
}
public Object handleEmptyBody(Object body, HttpInputMessage inputMessage, MethodParameter parameter,
Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
return body ;
}
}
注:这并非真的AOP技术(并没有产生代理),只是在读取数据之前执行了该操作。
如果有必要你可以在上面的supports放中进行条件判断,只有符合条件的才进行处理。
以上是正确的2种方式实现修改Request Body内容。是否还有其它技术实现呢?拦截器是可以呢?接下来我们通过拦截器的方式进行尝试修改内容。
2.4 使用拦截器
拦截器可以拦截传入的 HTTP 请求,并在控制器处理这些请求之前对其进行处理。拦截器有多种用途,如身份验证、授权、日志记录和缓存。此外,拦截器是 Spring MVC 框架的特有功能,它们可以访问 Spring ApplicationContext,如下是拦截器的工作原理:
DispatcherServlet 会将 HTTP 请求转发给拦截器。此外,在处理之后,拦截器可以将请求转发给控制器或拒绝该请求。
public static class EscapeHtmlRequestInterceptor implements HandlerInterceptor {
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
EscapeHtmlRequestWrapper htmlEscapeRequestWrapper = new EscapeHtmlRequestWrapper(request);
// 其实写到这你就应该想到这里的EscapeHtmlRequestWrapper 是如何传递下去的呢?
// 这里的返回值仅仅是boolean类型是否要继续请求而已。
// 这里我们姑且像下面这样调用父类的方法吧
return HandlerInterceptor.super.preHandle(htmlEscapeRequestWrapper, response, handler) ;
}
}
// 注册拦截器
@Component
public class WebMvcConfiguration implements WebMvcConfigurer {
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new EscapeHtmlRequestInterceptor()).addPathPatterns("/**");
}
}
测试请求
控制台抛出了错误,客户端返回了400状态码。
这也就说明了,通过拦截器是无法实现修改Request Body内容的。
以上是本篇文章的全部内容,如对你有帮助帮忙点赞+转发+收藏
推荐文章
Jackson才是王!SpringBoot优雅的控制JSON数据
优雅!SpringBoot通过函数式编程模型声明Restful API接口
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)