大数跨境
0
0

在SpringBoot中拦截修改请求Body的2种正确方式

在SpringBoot中拦截修改请求Body的2种正确方式 Spring全家桶实战案例
2024-08-24
0
导读:在Spring Boot中请求到达Controller之前修改请求Body的2种方式

环境: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 字符:

@RestControllerAdvicepublic 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) ;  }}// 注册拦截器@Componentpublic class WebMvcConfiguration implements WebMvcConfigurer {  public void addInterceptors(InterceptorRegistry registry) {    registry.addInterceptor(new EscapeHtmlRequestInterceptor()).addPathPatterns("/**");  }}

测试请求

控制台抛出了错误,客户端返回了400状态码。

这也就说明了,通过拦截器是无法实现修改Request Body内容的。

以上是本篇文章的全部内容,如对你有帮助帮忙点赞+转发+收藏

推荐文章

Jackson才是王!SpringBoot优雅的控制JSON数据

高性能缓存神器Caffeine

建议收藏!SpringBoot项目实战开发技巧

基于SpringBoot支持任意文件在线预览

放弃ThreadLocal!TTL真香

别不信@PathVariable你真不会用

优雅!SpringBoot通过函数式编程模型声明Restful API接口

新一代web框架WebFlux到底要不要学?

想成为高手?SpringBoot这些事件你必须掌握

优雅实现API接口开关:让你的应用更可控

全网最详细的OpenFeign讲解,肯定有你不知道的

优化技巧:如何加快Spring项目启动速度

你了解Spring AOP的这个技能点吗?有什么应用场景?

如何利用ElasticSearch动态创建索引并批量保存数据,轻松提升工作效率

深入ReactiveFeign:反应式远程接口调用的最佳实践

【声明】内容源于网络
0
0
Spring全家桶实战案例
Java全栈开发,前端Vue2/3全家桶;Spring, SpringBoot 2/3, Spring Cloud各种实战案例及源码解读
内容 832
粉丝 0
Spring全家桶实战案例 Java全栈开发,前端Vue2/3全家桶;Spring, SpringBoot 2/3, Spring Cloud各种实战案例及源码解读
总阅读285
粉丝0
内容832